Compare commits

...

48 Commits

Author SHA1 Message Date
MariaZ bee6b145ed Add test checking if StatsTable is shown on button click 2022-09-20 13:42:21 +02:00
MariaZ 57c8e31a9a Fix image import mocking 2022-09-20 13:36:05 +02:00
MariaZ 726b65914b Move react-i18next mock to jest-setup.js 2022-09-20 12:37:41 +02:00
MariaZ 82b6a6b603 Move jest-setup.js to ./test folder 2022-09-20 12:37:13 +02:00
MariaZ 5f61f37d2f Add tests for Stats component 2022-09-20 12:31:54 +02:00
MariaZ 1b6d21f730 Add check for undefined & null data in Stats component 2022-09-20 12:31:27 +02:00
MariaZ be0c11abfe Add tests for StatsTabe component 2022-09-20 11:58:34 +02:00
MariaZ e2e320927b Add tests for StatsOverview component 2022-09-20 10:59:28 +02:00
MariaZ 5c2a80c5c2 Add tests for AppHelp component 2022-09-20 10:48:31 +02:00
MariaZ 2e060d3261 Recreate components folder structure for tests 2022-09-20 10:35:28 +02:00
MariaZ c267e80424 Remove unnecessary import of labels 2022-09-20 10:29:40 +02:00
MariaZ c95e25a9b2 Remove onClose required prop from AppHelp 2022-09-19 21:31:35 +02:00
MariaZ 6c1ee3e5e2 Add explainer text to stats page and move more stats to modal 2022-09-19 21:31:17 +02:00
MariaZ 499e2d0628 Add en translations for stats page 2022-09-19 21:30:51 +02:00
MariaZ 951fb778d4 Add * to cell with standard deviation 2022-09-19 21:23:03 +02:00
MariaZ 6d6473ca78 Add translations to StatsTable 2022-09-19 21:17:09 +02:00
MariaZ 5a0321c5e5 Update styling & add onClose to StatsTable to make it modal 2022-09-19 21:14:06 +02:00
MariaZ 0c6c706274 Add AppHelp component 2022-09-19 20:52:44 +02:00
Maria Zadnepryanets 8774d251de Merge branch 'chore/add-statistics' into 'main'
Add more data to stats

Closes #264

See merge request bloodyhealth/drip!472
2022-09-19 14:23:56 +00:00
Maria Zadnepryanets 974d081f40 Add more data to stats 2022-09-19 14:23:55 +00:00
Sofiya Tepikin c847270159 Merge branch 'chore/extend-license-test' into 'main'
Chore/extend license test

See merge request bloodyhealth/drip!539
2022-09-19 10:54:34 +00:00
Sofiya Tepikin d58c230eda Chore/extend license test 2022-09-19 10:54:33 +00:00
Sofiya Tepikin 0747ea8a5f Merge branch 'dependabot-npm_and_yarn-babel-eslint-parser-7.19.1' into 'main'
Chore(deps-dev): bump @babel/eslint-parser from 7.16.3 to 7.19.1

See merge request bloodyhealth/drip!533
2022-09-19 09:46:54 +00:00
Sofiya Tepikin 0bba7afc6f Merge branch 'chore/retire-hyperlink' into 'main'
Chore/Retire hyperlink library

See merge request bloodyhealth/drip!538
2022-09-19 09:25:16 +00:00
Sofiya Tepikin f957553026 Chore/Retire hyperlink library 2022-09-19 09:25:16 +00:00
Maria Zadnepryanets 0597540b88 Merge branch 'fix/stop-keyboard-from-overlapping-with-inputs' into 'main'
Fix: Define isKeyboardOffset based on Platform

See merge request bloodyhealth/drip!523
2022-09-19 09:12:32 +00:00
Maria Zadnepryanets 75823ed750 Fix: Define isKeyboardOffset based on Platform 2022-09-19 09:12:31 +00:00
Sofiya Tepikin dd1c2cd96d Merge branch 'fix/license' into 'main'
Bring back License

See merge request bloodyhealth/drip!537
2022-09-18 13:54:56 +00:00
Sofiya Tepikin 06d346ee46 Bring back License 2022-09-18 15:51:39 +02:00
Sofiya Tepikin d4bd576cc9 Delete license.js 2022-09-18 13:49:24 +00:00
Sofiya Tepikin 85b3a8b4b6 Delete License.js 2022-09-18 13:46:21 +00:00
Sofiya Tepikin 206d4b06fa Merge branch 'fix/bring-back-license-test' into 'main'
Bring back the test

See merge request bloodyhealth/drip!536
2022-09-18 13:30:18 +00:00
Sofiya Tepikin 2669738c9d Bring back the test 2022-09-18 15:25:51 +02:00
Sofiya Tepikin 195c792837 Merge branch 'chore/unify-license-screens' into 'main'
Distinction between License and AcceptLicense screens

See merge request bloodyhealth/drip!534
2022-09-18 13:02:43 +00:00
Sofiya Tepikin f6a90994b6 Distinction between License and AcceptLicense screens 2022-09-18 13:02:43 +00:00
Sofiya Tepikin fb863c832b Merge branch 'chore/testing-library' into 'main'
Chore/testing library

See merge request bloodyhealth/drip!532
2022-09-18 10:32:10 +00:00
Sofiya Tepikin 7dd1a297a2 Chore/testing library 2022-09-18 10:32:10 +00:00
Sofiya Tepikin 1eadd1c5d6 Chore(deps-dev): bump @babel/eslint-parser from 7.16.3 to 7.19.1
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.16.3 to 7.19.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.19.1/eslint/babel-eslint-parser)
2022-09-18 09:07:33 +00:00
Sofiya Tepikin 3c7cb3dfff Merge branch 'chore/update-rn-0.67.4' into 'main'
Chore/update RN 0.67.4

See merge request bloodyhealth/drip!531
2022-09-18 05:54:28 +00:00
Sofiya Tepikin ef3a50ea76 Chore/update RN 0.67.4 2022-09-18 05:54:28 +00:00
Sofiya Tepikin 5166b65e81 Merge branch 'dependabot-npm_and_yarn-react-native-modal-datetime-picker-14.0.0' into 'main'
Bump react-native-modal-datetime-picker from 10.2.0 to 14.0.0

See merge request bloodyhealth/drip!514
2022-09-17 17:43:31 +00:00
Sofiya Tepikin 658ce1d074 Merge branch 'chore/update-rn-0.66.4' into 'main'
Chore/update RN 0.66.4

See merge request bloodyhealth/drip!528
2022-09-17 10:03:14 +00:00
Sofiya Tepikin 997332565f Chore/update RN 0.66.4 2022-09-17 10:03:14 +00:00
Sofiya Tepikin f1df68e973 Merge branch 'fix/temperature-screen' into 'main'
Fix: temperature screen

See merge request bloodyhealth/drip!525
2022-09-16 18:29:26 +00:00
Maria Zadnepryanets e289094d97 Fix: temperature screen 2022-09-16 18:29:26 +00:00
Sofiya Tepikin 2fb82d95e6 Merge branch 'chore/nodejs-mobile-new-api' into 'main'
Change the API to remove nodejs listeners

See merge request bloodyhealth/drip!524
2022-09-16 17:05:13 +00:00
Sofiya Tepikin 1610c8416a Change the API to remove nodejs listeners 2022-09-13 19:47:11 +02:00
Sofiya Tepikin 971ea4bdcf Bump react-native-modal-datetime-picker from 10.2.0 to 14.0.0
Bumps [react-native-modal-datetime-picker](https://github.com/mmazzarolo/react-native-modal-datetime-picker) from 10.2.0 to 14.0.0.
- [Release notes](https://github.com/mmazzarolo/react-native-modal-datetime-picker/releases)
- [Changelog](https://github.com/mmazzarolo/react-native-modal-datetime-picker/blob/master/.releaserc)
- [Commits](https://github.com/mmazzarolo/react-native-modal-datetime-picker/compare/v10.2.0...v14.0.0)
2022-09-11 09:07:11 +00:00
60 changed files with 2489 additions and 486 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"presets": ["module:metro-react-native-babel-preset"],
}
+2
View File
@@ -0,0 +1,2 @@
BUNDLE_PATH: "vendor/bundle"
BUNDLE_FORCE_RUBY_PLATFORM: 1
+1
View File
@@ -32,6 +32,7 @@ build/
.gradle
local.properties
*.iml
*.hprof
# node.js
#
+1
View File
@@ -0,0 +1 @@
2.7.4
+4
View File
@@ -0,0 +1,4 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.4'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
+96
View File
@@ -0,0 +1,96 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
rexml
activesupport (6.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
claide (1.1.0)
cocoapods (1.11.3)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.11.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.3)
activesupport (>= 5.0, < 7)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.5.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.1.9)
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
ffi (1.15.5)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (1.10.0)
concurrent-ruby (~> 1.0)
json (2.6.1)
minitest (5.15.0)
molinillo (0.8.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.6)
rexml (3.2.5)
ruby-macho (2.5.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
zeitwerk (2.5.4)
PLATFORMS
ruby
DEPENDENCIES
cocoapods (~> 1.11, >= 1.11.2)
RUBY VERSION
ruby 2.7.4p191
BUNDLED WITH
2.2.27
+11 -1
View File
@@ -114,12 +114,17 @@ def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for in debug.
*/
def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures")
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -164,6 +169,11 @@ android {
buildTypes {
debug {
signingConfig signingConfigs.debug
if (nativeArchitectures) {
ndk {
abiFilters nativeArchitectures.split(',')
}
}
}
release {
// Caution! In production, you need to generate your own keystore file.
+1 -1
View File
@@ -33,7 +33,7 @@
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="sensorPortrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
android:insetTop="@dimen/abc_edit_text_inset_top_material"
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
<selector>
<!--
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
-->
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
</selector>
</inset>
+1 -1
View File
@@ -3,7 +3,7 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:textColor">#000000</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
+9 -15
View File
@@ -7,7 +7,7 @@ buildscript {
}
ext.kotlinVersion = "1.3.10"
dependencies {
classpath("com.android.tools.build:gradle:4.2.1")
classpath("com.android.tools.build:gradle:4.2.2")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -17,8 +17,6 @@ buildscript {
allprojects {
repositories {
mavenLocal()
mavenCentral()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
@@ -27,6 +25,13 @@ allprojects {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google()
maven { url 'https://www.jitpack.io' }
maven {
@@ -48,16 +53,5 @@ ext {
minSdkVersion = 23
compileSdkVersion = 30
targetSdkVersion = 30
ndkVersion = "20.1.5948944"
}
subprojects {
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 29
buildToolsVersion '29.0.3'
}
}
}
ndkVersion = "21.4.7075529"
}
+5 -2
View File
@@ -9,7 +9,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
@@ -25,4 +25,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.93.0
FLIPPER_VERSION=0.99.0
# https://github.com/facebook/react-native/issues/30729
org.gradle.jvmargs=-Xmx4g
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
}
+43
View File
@@ -0,0 +1,43 @@
import React from 'react'
import PropTypes from 'prop-types'
import { BackHandler, StyleSheet, View } from 'react-native'
import { useTranslation } from 'react-i18next'
import License from './settings/License'
import Button from './common/button'
import { saveLicenseFlag } from '../local-storage'
import { Containers } from '../styles'
export default function AcceptLicense({ setLicense }) {
const onAcceptLicense = async () => {
await saveLicenseFlag()
setLicense()
}
const { t } = useTranslation()
return (
<License>
<View style={styles.container}>
<Button onPress={BackHandler.exitApp} testID="licenseCancelButton">
{t('labels.shared.cancel')}
</Button>
<Button isCTA onPress={onAcceptLicense} testID="licenseOkButton">
{t('labels.shared.ok')}
</Button>
</View>
</License>
)
}
AcceptLicense.propTypes = {
setLicense: PropTypes.func.isRequired,
}
const styles = StyleSheet.create({
container: {
...Containers.rowContainer,
},
})
-51
View File
@@ -1,51 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { BackHandler, StyleSheet, View } from 'react-native'
import { useTranslation } from 'react-i18next'
import AppPage from './common/app-page'
import AppText from './common/app-text'
import Button from './common/button'
import Segment from './common/segment'
import { saveLicenseFlag } from '../local-storage'
import { Containers } from '../styles'
export default function License({ setLicense }) {
const onAcceptLicense = async () => {
await saveLicenseFlag()
setLicense()
}
const { t } = useTranslation()
const currentYear = new Date().getFullYear()
return (
<AppPage testID="licensePage">
<Segment last testID="test" title={t('settings.license.title')}>
<AppText testID="test">
{t('settings.license.text', { currentYear })}
</AppText>
<View style={styles.container}>
<Button onPress={BackHandler.exitApp} testID="licenseCancelButton">
{t('labels.shared.cancel')}
</Button>
<Button isCTA onPress={onAcceptLicense} testID="licenseOkButton">
{t('labels.shared.ok')}
</Button>
</View>
</Segment>
</AppPage>
)
}
License.propTypes = {
setLicense: PropTypes.func.isRequired,
}
const styles = StyleSheet.create({
container: {
...Containers.rowContainer,
},
})
+2 -2
View File
@@ -7,7 +7,7 @@ import { openDb } from '../db'
import App from './app'
import AppLoadingView from './common/app-loading'
import AppStatusBar from './common/app-status-bar'
import License from './License'
import AcceptLicense from './AcceptLicense'
import PasswordPrompt from './password-prompt'
export default function AppWrapper() {
@@ -38,7 +38,7 @@ export default function AppWrapper() {
}
if (!isLicenseAccepted) {
return <License setLicense={() => setIsLicenseAccepted(true)} />
return <AcceptLicense setLicense={() => setIsLicenseAccepted(true)} />
}
return (
+32
View File
@@ -0,0 +1,32 @@
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, View } from 'react-native'
import AppText from '../common/app-text'
import { Containers, Spacing, Typography } from '../../styles'
const AppHelp = ({ text }) => (
<View style={styles.container}>
<AppText style={styles.accentPurple}>*</AppText>
<AppText>{text}</AppText>
</View>
)
AppHelp.propTypes = {
text: PropTypes.string.isRequired,
}
const styles = StyleSheet.create({
accentPurple: {
...Typography.accentPurple,
alignSelf: 'flex-start',
paddingRight: Spacing.base,
},
container: {
...Containers.rowContainer,
padding: Spacing.base,
},
})
export default AppHelp
+28
View File
@@ -0,0 +1,28 @@
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, Text, Linking } from 'react-native'
import { Colors, Typography } from '../../styles'
const AppLink = ({ children, url, ...props }) => {
return (
<Text style={styles.link} {...props} onPress={() => Linking.openURL(url)}>
{children}
</Text>
)
}
AppLink.propTypes = {
children: PropTypes.node,
url: PropTypes.string,
}
const styles = StyleSheet.create({
link: {
...Typography.mainText,
color: Colors.purple,
textDecorationLine: 'underline',
},
})
export default AppLink
@@ -6,21 +6,21 @@ import AppText from './app-text'
import { Sizes, Spacing, Typography } from '../../styles'
const Table = ({ tableContent }) => {
return tableContent.map((rowContent, i) => (
<Row key={i} rowContent={rowContent} />
))
const StatsOverview = ({ data }) => {
return data.map((rowContent, i) => <Row key={i} rowContent={rowContent} />)
}
Table.propTypes = {
tableContent: PropTypes.array.isRequired,
StatsOverview.propTypes = {
data: PropTypes.array.isRequired,
}
const Row = ({ rowContent }) => {
const showHelp = rowContent[1].includes('deviation') ? true : false
return (
<View style={styles.row}>
<Cell content={rowContent[0]} isLeft />
<Cell content={rowContent[1]} />
<Cell content={rowContent[1]} showHelp={showHelp} />
</View>
)
}
@@ -29,7 +29,7 @@ Row.propTypes = {
rowContent: PropTypes.array.isRequired,
}
const Cell = ({ content, isLeft }) => {
const Cell = ({ content, isLeft, showHelp }) => {
const styleContainer = isLeft ? styles.cellLeft : styles.cellRight
const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange
const numberOfLines = isLeft ? 1 : 2
@@ -44,6 +44,7 @@ const Cell = ({ content, isLeft }) => {
>
{content}
</AppText>
{showHelp && <AppText style={styles.accentPurpleBig}>*</AppText>}
</View>
)
}
@@ -51,6 +52,7 @@ const Cell = ({ content, isLeft }) => {
Cell.propTypes = {
content: PropTypes.node.isRequired,
isLeft: PropTypes.bool,
showHelp: PropTypes.bool,
}
const styles = StyleSheet.create({
@@ -65,18 +67,14 @@ const styles = StyleSheet.create({
},
cellLeft: {
alignItems: 'flex-end',
flex: 5,
flex: 3,
justifyContent: 'center',
},
cellRight: {
flex: 5,
justifyContent: 'center',
},
row: {
flexDirection: 'row',
marginBottom: Spacing.tiny,
marginLeft: Spacing.tiny,
},
row: { flexDirection: 'row' },
})
export default Table
export default StatsOverview
+136
View File
@@ -0,0 +1,136 @@
import React from 'react'
import { Dimensions, FlatList, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import AppText from './app-text'
import CloseIcon from './close-icon'
import cycleModule from '../../lib/cycle'
import { Sizes, Spacing, Typography, Colors } from '../../styles'
import { humanizeDate } from '../helpers/format-date'
const Item = ({ data }) => {
const { t } = useTranslation(null, { keyPrefix: 'plurals' })
if (!data) return false
const { date, cycleLength, bleedingLength } = data
return (
<View style={styles.row}>
<View style={styles.accentCell}>
<AppText>{humanizeDate(date)}</AppText>
</View>
<View style={styles.cell}>
<AppText>{t('day', { count: cycleLength })}</AppText>
</View>
<View style={styles.cell}>
<AppText>{t('day', { count: bleedingLength })}</AppText>
</View>
</View>
)
}
Item.propTypes = {
data: PropTypes.object.isRequired,
}
const StatsTable = ({ onClose }) => {
const renderItem = ({ item }) => <Item data={item} />
const data = cycleModule().getStats()
if (!data || data.length === 0) return false
return (
<View style={styles.modalContainer}>
<View style={styles.headerContainer}>
<CloseIcon onClose={onClose} />
</View>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.date}
ItemSeparatorComponent={ItemDivider}
ListHeaderComponent={FlatListHeader}
ListHeaderComponentStyle={styles.headerDivider}
stickyHeaderIndices={[0]}
contentContainerStyle={styles.container}
/>
</View>
)
}
StatsTable.propTypes = {
onClose: PropTypes.func,
}
const ItemDivider = () => <View style={styles.divider} />
const FlatListHeader = () => {
const { t } = useTranslation(null, { keyPrefix: 'stats' })
return (
<View style={styles.row}>
<View style={styles.accentCell}>
<AppText style={styles.header}>{t('cycle_start')}</AppText>
</View>
<View style={styles.cell}>
<AppText style={styles.header}>{t('cycle_length')}</AppText>
</View>
<View style={styles.cell}>
<AppText style={styles.header}>{t('bleeding')}</AppText>
</View>
</View>
)
}
const styles = StyleSheet.create({
accentCell: {
flex: 3,
justifyContent: 'center',
},
cell: {
flex: 2,
justifyContent: 'center',
},
container: {
paddingHorizontal: Spacing.base,
},
divider: {
height: 1,
width: '100%',
backgroundColor: Colors.grey,
},
header: {
...Typography.accentOrange,
paddingVertical: Spacing.small,
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingTop: Spacing.base,
paddingRight: Spacing.base,
},
headerDivider: {
borderBottomColor: Colors.purple,
borderBottomWidth: 2,
},
modalContainer: {
alignSelf: 'center',
backgroundColor: Colors.turquoiseLight,
marginTop: Sizes.huge * 2,
maxHeight: Dimensions.get('window').height * 0.7,
minHeight: '40%',
position: 'absolute',
width: '100%',
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: Spacing.tiny,
backgroundColor: Colors.turquoiseLight,
},
})
export default StatsTable
+2 -5
View File
@@ -4,7 +4,7 @@ import { ScrollView, StyleSheet, View } from 'react-native'
import AppText from '../common/app-text'
import { Colors, Typography } from '../../styles'
import { Colors, Containers, Typography } from '../../styles'
const AppPage = ({
children,
@@ -35,10 +35,7 @@ AppPage.propTypes = {
}
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.turquoiseLight,
flex: 1,
},
container: { ...Containers.pageContainer },
scrollView: {
backgroundColor: Colors.turquoiseLight,
flexGrow: 1,
+4 -19
View File
@@ -1,30 +1,15 @@
import React from 'react'
import { KeyboardAvoidingView, StyleSheet, TextInput } from 'react-native'
import { StyleSheet, TextInput } from 'react-native'
import PropTypes from 'prop-types'
import { Colors, Spacing, Typography } from '../../styles'
const AppTextInput = ({ style, isKeyboardOffset, ...props }) => {
const behavior = isKeyboardOffset ? 'padding' : 'height'
const keyboardVerticalOffset = isKeyboardOffset ? 300 : 0
return (
<KeyboardAvoidingView
behavior={behavior}
keyboardVerticalOffset={keyboardVerticalOffset}
>
<TextInput style={[styles.input, style]} {...props} />
</KeyboardAvoidingView>
)
}
const AppTextInput = ({ style, ...props }) => (
<TextInput style={[styles.input, style]} {...props} />
)
AppTextInput.propTypes = {
style: PropTypes.object,
isKeyboardOffset: PropTypes.bool,
}
AppTextInput.defultProps = {
isKeyboardOffset: true,
}
const styles = StyleSheet.create({
+4 -10
View File
@@ -2,24 +2,18 @@ import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, Text } from 'react-native'
import Link from './link'
import { Colors, Typography } from '../../styles'
const AppText = ({ children, linkStyle, style, ...props }) => {
// we parse for links in case the text contains any
const AppText = ({ children, style, ...props }) => {
return (
<Link style={linkStyle}>
<Text style={[styles.text, style]} {...props}>
{children}
</Text>
</Link>
<Text style={[styles.text, style]} {...props}>
{children}
</Text>
)
}
AppText.propTypes = {
children: PropTypes.node,
linkStyle: PropTypes.object,
style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
}
-40
View File
@@ -1,40 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Hyperlink from 'react-native-hyperlink'
import { StyleSheet } from 'react-native'
import { Colors, Typography } from '../../styles'
import links from '../../i18n/en/links'
const Link = ({ children, style }) => {
return (
<Hyperlink
linkStyle={[styles.link, style]}
linkText={replaceUrlWithText}
linkDefault
>
{children}
</Hyperlink>
)
}
Link.propTypes = {
children: PropTypes.node,
style: PropTypes.object,
}
const styles = StyleSheet.create({
link: {
color: Colors.purple,
textDecorationLine: 'underline',
...Typography.mainText,
},
})
function replaceUrlWithText(url) {
const link = Object.values(links).find((l) => l.url === url)
return (link && link.text) || url
}
export default Link
+3 -8
View File
@@ -4,7 +4,7 @@ import { StyleSheet, View } from 'react-native'
import AppText from './app-text'
import { Colors, Spacing, Typography } from '../../styles'
import { Colors, Containers, Spacing, Typography } from '../../styles'
const Segment = ({ children, last, title }) => {
const containerStyle = last ? styles.containerLast : styles.container
@@ -25,21 +25,16 @@ Segment.propTypes = {
title: PropTypes.string,
}
const segmentContainer = {
marginHorizontal: Spacing.base,
marginBottom: Spacing.base,
}
const styles = StyleSheet.create({
container: {
borderStyle: 'solid',
borderBottomWidth: 1,
borderBottomColor: Colors.greyLight,
paddingBottom: Spacing.base,
...segmentContainer,
...Containers.segmentContainer,
},
containerLast: {
...segmentContainer,
...Containers.segmentContainer,
},
title: {
...Typography.subtitle,
+110 -92
View File
@@ -109,101 +109,109 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
}
const iconName = shouldShowInfo ? 'chevron-up' : 'chevron-down'
const noteText = symptom === 'note' ? data.value : data.note
const inputProps = {
multiline: true,
numberOfLines: 3,
scrollEnabled: false,
style: styles.input,
textAlignVertical: 'top',
}
return (
<AppModal onClose={onSave}>
<ScrollView
contentContainerStyle={styles.modalContainer}
style={styles.modalWindow}
>
<View style={styles.modalWindow}>
<View style={styles.headerContainer}>
<CloseIcon onClose={onSave} />
</View>
{symptom === 'temperature' && (
<Temperature
date={date}
data={data}
save={(value, field) => onSaveTemperature(value, field)}
/>
)}
{shouldShow(symptomConfig.selectTabGroups) &&
symtomPage[symptom].selectTabGroups.map((group) => {
return (
<Segment key={group.key} style={styles.segmentBorder}>
<AppText style={styles.title}>{group.title}</AppText>
<SelectTabGroup
activeButton={data[group.key]}
buttons={group.options}
onSelect={(value) => onSelectTab(group, value)}
/>
</Segment>
)
})}
{shouldShow(symptomConfig.selectBoxGroups) &&
symtomPage[symptom].selectBoxGroups.map((group) => {
const isOtherSelected =
data['other'] !== null &&
data['other'] !== false &&
Object.keys(group.options).includes('other')
return (
<Segment key={group.key} style={styles.segmentBorder}>
<AppText style={styles.title}>{group.title}</AppText>
<SelectBoxGroup
labels={group.options}
onSelect={(value) => onSelectBox(value)}
optionsState={data}
/>
{isOtherSelected && (
<AppTextInput
multiline={true}
placeholder={sharedLabels.enter}
value={data.note}
onChangeText={(value) => onSelectBoxNote(value)}
<ScrollView
contentContainerStyle={styles.modalContainer}
keyboardDismissMode="on-drag"
>
{symptom === 'temperature' && (
<Temperature
date={date}
data={data}
save={(value, field) => onSaveTemperature(value, field)}
/>
)}
{shouldShow(symptomConfig.selectTabGroups) &&
symtomPage[symptom].selectTabGroups.map((group) => {
return (
<Segment key={group.key} style={styles.segmentBorder}>
<AppText style={styles.title}>{group.title}</AppText>
<SelectTabGroup
activeButton={data[group.key]}
buttons={group.options}
onSelect={(value) => onSelectTab(group, value)}
/>
)}
</Segment>
)
})}
{shouldShow(symptomConfig.excludeText) && (
<Segment style={styles.segmentBorder}>
<AppSwitch
onToggle={onExcludeToggle}
text={symtomPage[symptom].excludeText}
value={data.exclude}
/>
</Segment>
)}
{shouldShow(symptomConfig.note) && (
<Segment style={styles.segmentBorder}>
<AppText>{symtomPage[symptom].note}</AppText>
<AppTextInput
multiline={true}
numberOfLines={3}
onChangeText={onEditNote}
placeholder={sharedLabels.enter}
testID="noteInput"
value={noteText !== null ? noteText : ''}
/>
</Segment>
)}
<View style={styles.buttonsContainer}>
<Button iconName={iconName} isSmall onPress={onPressLearnMore}>
{sharedLabels.learnMore}
</Button>
<Button isSmall onPress={onRemove}>
{sharedLabels.remove}
</Button>
<Button isCTA isSmall onPress={onSave}>
{sharedLabels.save}
</Button>
</View>
{shouldShowInfo && (
<Segment last style={styles.segmentBorder}>
<AppText>{info[symptom].text}</AppText>
</Segment>
)}
</ScrollView>
</Segment>
)
})}
{shouldShow(symptomConfig.selectBoxGroups) &&
symtomPage[symptom].selectBoxGroups.map((group) => {
const isOtherSelected =
data['other'] !== null &&
data['other'] !== false &&
Object.keys(group.options).includes('other')
return (
<Segment key={group.key} style={styles.segmentBorder}>
<AppText style={styles.title}>{group.title}</AppText>
<SelectBoxGroup
labels={group.options}
onSelect={(value) => onSelectBox(value)}
optionsState={data}
/>
{isOtherSelected && (
<AppTextInput
{...inputProps}
placeholder={sharedLabels.enter}
value={data.note}
onChangeText={(value) => onSelectBoxNote(value)}
/>
)}
</Segment>
)
})}
{shouldShow(symptomConfig.excludeText) && (
<Segment style={styles.segmentBorder}>
<AppSwitch
onToggle={onExcludeToggle}
text={symtomPage[symptom].excludeText}
value={data.exclude}
/>
</Segment>
)}
{shouldShow(symptomConfig.note) && (
<Segment style={styles.segmentBorder}>
<AppText>{symtomPage[symptom].note}</AppText>
<AppTextInput
{...inputProps}
onChangeText={onEditNote}
placeholder={sharedLabels.enter}
testID="noteInput"
value={noteText !== null ? noteText : ''}
/>
</Segment>
)}
<View style={styles.buttonsContainer}>
<Button iconName={iconName} isSmall onPress={onPressLearnMore}>
{sharedLabels.learnMore}
</Button>
<Button isSmall onPress={onRemove}>
{sharedLabels.remove}
</Button>
<Button isCTA isSmall onPress={onSave}>
{sharedLabels.save}
</Button>
</View>
{shouldShowInfo && (
<Segment last style={styles.segmentBorder}>
<AppText>{info[symptom].text}</AppText>
</Segment>
)}
</ScrollView>
</View>
</AppModal>
)
}
@@ -218,21 +226,31 @@ SymptomEditView.propTypes = {
const styles = StyleSheet.create({
buttonsContainer: {
...Containers.rowContainer,
paddingHorizontal: Spacing.base,
paddingBottom: Spacing.base,
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingVertical: Spacing.tiny,
paddingTop: Spacing.base,
paddingHorizontal: Spacing.base,
position: 'absolute',
width: '100%',
zIndex: 3, // works on ios
elevation: 3, // works on android
},
input: {
height: Sizes.base * 5,
},
modalContainer: {
flex: 1,
padding: Spacing.base,
paddingHorizontal: Spacing.base,
},
modalWindow: {
alignSelf: 'center',
backgroundColor: 'white',
borderRadius: 10,
marginVertical: Sizes.huge * 2,
marginTop: Sizes.huge * 2,
paddingTop: Spacing.large * 2,
position: 'absolute',
minHeight: '40%',
maxHeight: Dimensions.get('window').height * 0.7,
+15
View File
@@ -22,3 +22,18 @@ export function dateToTitle(dateString) {
? labels.today
: moment(dateString).format('ddd DD. MMM YY')
}
export function humanizeDate(dateString) {
if (!dateString) return ''
const today = LocalDate.now()
try {
const dateToDisplay = LocalDate.parse(dateString)
return today.equals(dateToDisplay)
? labels.today
: moment(dateString).format('DD. MMM YY')
} catch (e) {
return ''
}
}
+20 -19
View File
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { Alert, StyleSheet, View } from 'react-native'
import { Alert, KeyboardAvoidingView, StyleSheet, View } from 'react-native'
import nodejs from 'nodejs-mobile-react-native'
import AppPage from './common/app-page'
@@ -36,9 +36,9 @@ const PasswordPrompt = ({ enableShowApp }) => {
}
useEffect(() => {
nodejs.channel.addListener('check-pw', passHashToDb, this)
const listener = nodejs.channel.addListener('check-pw', passHashToDb, this)
return () => nodejs.channel.remove('check-pw', passHashToDb)
return () => listener.remove()
}, [])
const onDeleteDataConfirmation = async () => {
@@ -68,22 +68,23 @@ const PasswordPrompt = ({ enableShowApp }) => {
<>
<Header isStatic />
<AppPage contentContainerStyle={styles.contentContainer}>
<AppTextInput
isKeyboardOffset={false}
onChangeText={setPassword}
secureTextEntry={true}
placeholder={labels.enterPassword}
/>
<View style={styles.containerButtons}>
<Button onPress={onConfirmDeletion}>{labels.forgotPassword}</Button>
<Button
disabled={!isPasswordEntered}
isCTA={isPasswordEntered}
onPress={unlockApp}
>
{labels.title}
</Button>
</View>
<KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={150}>
<AppTextInput
onChangeText={setPassword}
secureTextEntry={true}
placeholder={labels.enterPassword}
/>
<View style={styles.containerButtons}>
<Button onPress={onConfirmDeletion}>{labels.forgotPassword}</Button>
<Button
disabled={!isPasswordEntered}
isCTA={isPasswordEntered}
onPress={unlockApp}
>
{labels.title}
</Button>
</View>
</KeyboardAvoidingView>
</AppPage>
</>
)
@@ -3,6 +3,7 @@ import { Platform, Linking } from 'react-native'
import AppPage from '../common/app-page'
import AppText from '../common/app-text'
import AppLink from '../common/AppLink'
import Segment from '../common/segment'
import Button from '../common/button'
import ButtonRow from '../common/button-row'
@@ -35,13 +36,15 @@ const AboutSection = () => {
</Segment>
<Segment title={t('credits.title')}>
<AppText>
{t('credits.text', {
smashicons: links.smashicons.url,
pause08: links.pause08.url,
kazachek: links.kazachek.url,
freepik: links.freepik.url,
flaticon: links.flaticon.url,
})}
{t('credits.text')}{' '}
<AppLink url={links.flaticon.url}>flaticon</AppLink>.{' '}
</AppText>
<AppText>
{t('credits.madeBy')}{' '}
<AppLink url={links.smashicons.url}>smashicons</AppLink>,{' '}
<AppLink url={links.pause08.url}>pause08</AppLink>,{' '}
<AppLink url={links.kazachek.url}>kazachek</AppLink>,{' '}
<AppLink url={links.freepik.url}>freepik</AppLink>.
</AppText>
</Segment>
<Segment title={t('donate.title')}>
+29
View File
@@ -0,0 +1,29 @@
import React from 'react'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import AppPage from '../common/app-page'
import AppText from '../common/app-text'
import AppLink from '../common/AppLink'
import Segment from '../common/segment'
const License = ({ children }) => {
const { t } = useTranslation(null, { keyPrefix: 'settings.license' })
const currentYear = new Date().getFullYear()
const link = 'https://www.gnu.org/licenses/gpl-3.0.html'
return (
<AppPage title={t('title')}>
<Segment last>
<AppText>{t('text', { currentYear })}</AppText>
<AppLink url={link}>{link}</AppLink>
{children}
</Segment>
</AppPage>
)
}
License.propTypes = {
children: PropTypes.node,
}
export default License
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { Alert, StyleSheet, View } from 'react-native'
import { Alert, KeyboardAvoidingView, StyleSheet, View } from 'react-native'
import nodejs from 'nodejs-mobile-react-native'
import AppTextInput from '../../common/app-text-input'
@@ -24,10 +24,12 @@ const ConfirmWithPassword = ({ onSuccess, onCancel }) => {
}
useEffect(() => {
nodejs.channel.addListener('password-check', checkPassword, this)
return () => {
nodejs.channel.removeListener('password-check', checkPassword)
}
const listener = nodejs.channel.addListener(
'password-check',
checkPassword,
this
)
return () => listener.remove()
}, [])
const onIncorrectPassword = () => {
@@ -51,7 +53,7 @@ const ConfirmWithPassword = ({ onSuccess, onCancel }) => {
const isPassword = password !== null
return (
<>
<KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={150}>
<AppTextInput
onChangeText={setPassword}
placeholder={labels.enterCurrent}
@@ -68,7 +70,7 @@ const ConfirmWithPassword = ({ onSuccess, onCancel }) => {
{shared.confirmToProceed}
</Button>
</View>
</>
</KeyboardAvoidingView>
)
}
+2 -2
View File
@@ -2,8 +2,8 @@ import Reminders from './reminders/reminders'
import NfpSettings from './nfp-settings'
import DataManagement from './data-management'
import Password from './password'
import About from './about'
import License from './license'
import About from './About'
import License from './License'
import PrivacyPolicy from './privacy-policy'
export default {
-21
View File
@@ -1,21 +0,0 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import AppPage from '../common/app-page'
import AppText from '../common/app-text'
import Segment from '../common/segment'
const License = () => {
const { t } = useTranslation()
const currentYear = new Date().getFullYear()
return (
<AppPage title={t('settings.license.title')}>
<Segment last>
<AppText>{t('settings.license.text', { currentYear })}</AppText>
</Segment>
</AppPage>
)
}
export default License
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'
import { StyleSheet } from 'react-native'
import { KeyboardAvoidingView, StyleSheet } from 'react-native'
import nodejs from 'nodejs-mobile-react-native'
import PropTypes from 'prop-types'
@@ -41,9 +41,8 @@ const EnterNewPassword = ({ changeEncryptionAndRestart }) => {
const isButtonActive = password.length > 0 && passwordConfirmation.length > 0
return (
<>
<KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={150}>
<AppTextInput
isKeyboardOffset={false}
onChangeText={setPassword}
placeholder={labels.enterNew}
textContentType="password"
@@ -51,7 +50,6 @@ const EnterNewPassword = ({ changeEncryptionAndRestart }) => {
secureTextEntry={true}
/>
<AppTextInput
isKeyboardOffset={false}
onChangeText={setPasswordConfirmation}
placeholder={labels.confirmPassword}
textContentType="password"
@@ -68,7 +66,7 @@ const EnterNewPassword = ({ changeEncryptionAndRestart }) => {
>
{labels.savePassword}
</Button>
</>
</KeyboardAvoidingView>
)
}
+77 -53
View File
@@ -1,68 +1,89 @@
import React from 'react'
import { ImageBackground, View } from 'react-native'
import React, { useState } from 'react'
import { ImageBackground, SafeAreaView, ScrollView, View } from 'react-native'
import { ScaledSheet } from 'react-native-size-matters'
import { useTranslation } from 'react-i18next'
import AppPage from './common/app-page'
import Button from './common/button'
import AppHelp from './common/AppHelp'
import AppModal from './common/app-modal'
import AppText from './common/app-text'
import Segment from './common/segment'
import Table from './common/table'
import StatsOverview from './common/StatsOverview'
import StatsTable from './common/StatsTable'
import cycleModule from '../lib/cycle'
import {getCycleLengthStats as getCycleInfo} from '../lib/cycle-length'
import {stats as labels} from '../i18n/en/labels'
import { getCycleLengthStats as getCycleInfo } from '../lib/cycle-length'
import { Sizes, Spacing, Typography } from '../styles'
import { Containers, Sizes, Spacing, Typography } from '../styles'
const image = require('../assets/cycle-icon.png')
const Stats = () => {
const [isStatsVisible, setIsStatsVisible] = useState(false)
const { t } = useTranslation(null, { keyPrefix: 'stats' })
const cycleLengths = cycleModule().getAllCycleLengths()
const numberOfCycles = cycleLengths.length
const numberOfCycles = cycleLengths?.length
const hasAtLeastOneCycle = numberOfCycles >= 1
const cycleData = hasAtLeastOneCycle ? getCycleInfo(cycleLengths)
const cycleData = hasAtLeastOneCycle
? getCycleInfo(cycleLengths)
: { minimum: '—', maximum: '—', stdDeviation: '—' }
const statsData = [
[cycleData.minimum, labels.minLabel],
[cycleData.maximum, labels.maxLabel],
[cycleData.stdDeviation ? cycleData.stdDeviation : '—', labels.stdLabel],
[numberOfCycles, labels.basisOfStatsEnd]
[cycleData.minimum, t('min')],
[cycleData.maximum, t('max')],
[
cycleData.stdDeviation ? cycleData.stdDeviation : '—',
t('standard_deviation'),
],
[numberOfCycles, t('completed_cycles')],
]
return (
<AppPage contentContainerStyle={styles.pageContainer}>
<Segment last style={styles.pageContainer}>
<AppText>{labels.cycleLengthExplainer}</AppText>
{!hasAtLeastOneCycle && <AppText>{labels.emptyStats}</AppText>}
{hasAtLeastOneCycle &&
<View style={styles.container}>
<View style={styles.columnLeft}>
<ImageBackground
source={image}
imageStyle={styles.image}
style={styles.imageContainter}
>
<AppText
numberOfLines={1}
ellipsizeMode="clip"
style={styles.accentPurpleGiant}
<SafeAreaView style={styles.pageContainer}>
<ScrollView contentContainerStyle={styles.overviewContainer}>
<AppText>{t('cycle_length_explainer')}</AppText>
{!hasAtLeastOneCycle && <AppText>{t('no_data')}</AppText>}
{hasAtLeastOneCycle && (
<>
<View style={styles.container}>
<View style={styles.columnLeft}>
<ImageBackground
source={image}
imageStyle={styles.image}
style={styles.imageContainter}
>
{cycleData.mean}
</AppText>
<AppText style={styles.accentPurpleHuge}>
{labels.daysLabel}
</AppText>
</ImageBackground>
<AppText style={styles.accentOrange}>
{labels.averageLabel}
</AppText>
<AppText
numberOfLines={1}
ellipsizeMode="clip"
style={styles.accentPurpleGiant}
>
{cycleData.mean}
</AppText>
<AppText style={styles.accentPurpleHuge}>{t('days')}</AppText>
</ImageBackground>
<AppText style={styles.accentOrange}>{t('average')}</AppText>
</View>
<View style={styles.columnRight}>
<StatsOverview data={statsData} />
</View>
</View>
<View style={styles.columnRight}>
<Table tableContent={statsData} />
</View>
</View>
}
</Segment>
</AppPage>
<Button isCTA onPress={() => setIsStatsVisible(true)}>
{t('show_stats')}
</Button>
<AppHelp text={t('standard_deviation_help')} />
</>
)}
</ScrollView>
{isStatsVisible && (
<AppModal onClose={() => setIsStatsVisible(false)}>
<StatsTable
onClose={() => setIsStatsVisible(false)}
testID="statsTable"
/>
</AppModal>
)}
</SafeAreaView>
)
}
@@ -77,25 +98,24 @@ const styles = ScaledSheet.create({
},
accentPurpleGiant: {
...Typography.accentPurpleGiant,
marginTop: Spacing.base * (-2),
marginTop: Spacing.base * -2,
},
accentPurpleHuge: {
...Typography.accentPurpleHuge,
marginTop: Spacing.base * (-1),
marginTop: Spacing.base * -1,
},
container: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
paddingTop: Spacing.base
},
columnLeft: {
...column,
flex: 2,
flex: 3,
},
columnRight: {
...column,
flex: 3,
flex: 5,
paddingTop: Spacing.small,
},
image: {
@@ -105,9 +125,13 @@ const styles = ScaledSheet.create({
paddingTop: Spacing.large * 2.5,
marginBottom: Spacing.large,
},
overviewContainer: {
paddingHorizontal: Spacing.base,
paddingTop: Spacing.base,
},
pageContainer: {
marginTop: Spacing.base * 2,
}
...Containers.pageContainer,
},
})
export default Stats
+22 -2
View File
@@ -17,7 +17,8 @@
"about": {
"credits": {
"title": "Credits",
"text": "We love the drip. team. Thanks and lots of <3 to all of our condriputors. Thanks to Paula Härtel for the symptom tracking icons. All the other icons are made by {{smashicons}}, {{pause08}}, {{kazachek}} & {{freepik}} from {{flaticon}}."
"text": "We love the drip. team. Thanks and lots of <3 to all of our condriputors. Thanks to Paula Härtel for the symptom tracking icons. All the other icons from:",
"madeBy": "Made by:"
},
"donate": {
"button": "Donate here",
@@ -38,7 +39,7 @@
},
"license": {
"title": "drip. an open-source cycle tracking app",
"text": "Copyright (C) {{currentYear}} Heart of Code e.V.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/gpl-3.0.html."
"text": "Copyright (C) {{currentYear}} Heart of Code e.V.\n\nThis 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:"
},
"privacyPolicy": {
"title": "Privacy Policy",
@@ -59,5 +60,24 @@
"text": "You can read through the source code of drip. to ensure the given information is correct. The source code is like a recipe: It tells you how much and what kind of ingredients you need and how you prepare them to cook a tasty meal or program a funky app.\n\nBuon appetito!"
}
}
},
"plurals": {
"day": "{{count}} day",
"day_plural": "{{count}} days"
},
"stats": {
"show_stats": "Show period details",
"cycle_start": "Cycle start",
"cycle_length": "Cycle length",
"bleeding": "Bleeding",
"cycle_length_explainer": "Basic statistics about the length of your cycles.",
"no_data": "At least one completed cycle is needed to display stats.",
"days": "days",
"completed_cycles": "completed\ncycles",
"average": "Average cycle",
"min": "Shortest",
"max": "Longest",
"standard_deviation": "Standard\ndeviation",
"standard_deviation_help": "Based on the standard deviation of all your tracked periods drip. calculates a range for the starting day of the upcoming 3 periods. The range will be 3 days if your standard deviation is smaller than 1.5 and 5 days if the value is bigger.\n\nThe standard deviation tells you how much the length of your periods vary, 0 means all your periods are exactly the same length and the bigger the value the more the period length varies."
}
}
+1
View File
@@ -19,5 +19,6 @@ target 'drip' do
post_install do |installer|
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
end
end
+2 -2
View File
@@ -1038,8 +1038,8 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(inherited)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
);
MTL_ENABLE_DEBUG_INFO = YES;
@@ -1094,8 +1094,8 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(inherited)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
preset: '@testing-library/react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
moduleNameMapper: {
'\\.(png)$': require.resolve('./test/file-mock.js'),
},
setupFilesAfterEnv: ['./test/jest-setup.js'],
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)',
],
}
+23 -3
View File
@@ -3,6 +3,8 @@ 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
@@ -14,9 +16,13 @@ export default function config(opts) {
if (!opts) {
// we only want to require (and run) the db module
// when not running the tests
bleedingDaysSortedByDate = require('../db').getBleedingDaysSortedByDate()
cycleStartsSortedByDate = require('../db').getCycleStartsSortedByDate()
cycleDaysSortedByDate = require('../db').getCycleDaysSortedByDate()
bleedingDaysSortedByDate = toJSON(
require('../db').getBleedingDaysSortedByDate()
)
cycleStartsSortedByDate = toJSON(
require('../db').getCycleStartsSortedByDate()
)
cycleDaysSortedByDate = toJSON(require('../db').getCycleDaysSortedByDate())
maxBreakInBleeding = 1
maxCycleLength = 99
minCyclesForPrediction = 3
@@ -222,6 +228,19 @@ export default function config(opts) {
return predictedMenses
}
const getStats = () =>
cycleStartsSortedByDate.map((day, i) => {
const today = getTodayDate()
const cycleLength =
i === 0 ? getCycleDayNumber(today) : getAllCycleLengths()[i - 1]
return {
date: day.date,
cycleLength,
bleedingLength: ++getMensesDaysRightAfter(day).length,
}
})
return {
getCycleDayNumber,
getCycleForDay,
@@ -232,5 +251,6 @@ export default function config(opts) {
isMensesStart,
getMensesDaysRightAfter,
getCycleByStartDay,
getStats,
}
}
+8 -7
View File
@@ -14,7 +14,7 @@
"android": "react-native run-android",
"ios": "react-native run-ios --simulator=\"iPhone 8 Plus\"",
"log": "react-native log-android",
"test": "jest test && npm run lint",
"test": "jest test && yarn lint",
"test-watch": "jest --watch test",
"lint": "eslint components lib test styles db",
"devtool": "adb shell input keyevent 82",
@@ -44,12 +44,11 @@
"prop-types": "^15.8.1",
"react": "17.0.2",
"react-i18next": "^11.18.3",
"react-native": "0.65.2",
"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-hyperlink": "0.0.19",
"react-native-modal-datetime-picker": "10.2.0",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-push-notification": "3.2.1",
"react-native-share": "^7.9.0",
"react-native-simple-toast": "^1.1.3",
@@ -60,20 +59,22 @@
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/eslint-parser": "^7.16.3",
"@babel/eslint-parser": "^7.19.1",
"@babel/preset-react": "^7.16.0",
"@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",
"husky": "^8.0.0",
"jest": "^28.1.3",
"jetifier": "^1.6.6",
"metro-react-native-babel-preset": "^0.66.0",
"metro-react-native-babel-preset": "^0.66.2",
"prettier": "2.4.0",
"pretty-quick": "^3.1.1",
"react-native-codegen": "^0.0.7",
"react-native-version": "^3.1.0",
"react-test-renderer": "17.0.2",
"readline": "^1.3.0"
},
"description": "A menstrual cycle tracking app that's open-source and leaves your data on your phone. Use it to track your menstrual cycle or for fertility awareness!",
+14 -6
View File
@@ -9,7 +9,7 @@ export default {
marginTop: Spacing.small,
marginRight: Spacing.small,
paddingHorizontal: Spacing.small,
paddingVertical: Spacing.tiny
paddingVertical: Spacing.tiny,
},
boxActive: {
backgroundColor: Colors.orange,
@@ -17,17 +17,25 @@ export default {
centerItems: {
alignItems: 'center',
flex: 1,
justifyContent: 'center'
justifyContent: 'center',
},
pageContainer: {
backgroundColor: Colors.turquoiseLight,
flex: 1,
},
rowContainer: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between'
justifyContent: 'space-between',
},
selectGroupContainer: {
alignItems: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
marginVertical: Spacing.small
}
}
marginVertical: Spacing.small,
},
segmentContainer: {
marginHorizontal: Spacing.base,
marginBottom: Spacing.base,
},
}
+19 -15
View File
@@ -6,7 +6,7 @@ import Spacing from './spacing'
export const fonts = {
main: Platform.OS === 'ios' ? 'Jost-Book' : 'Jost-400-Book',
bold : Platform.OS === 'ios' ? 'Jost-Bold' : 'Jost-700-Bold',
bold: Platform.OS === 'ios' ? 'Jost-Bold' : 'Jost-700-Bold',
}
export const sizes = {
@@ -23,7 +23,7 @@ export const sizes = {
const accentText = {
fontFamily: fonts.bold,
textAlignVertical: 'center',
textTransform: 'uppercase'
textTransform: 'uppercase',
}
const accentTextBig = {
@@ -43,47 +43,51 @@ const accentTextHuge = {
const accentTextSmall = {
...accentText,
fontSize: sizes.small
fontSize: sizes.small,
}
const title = {
color: Colors.purple,
marginVertical: Spacing.large
marginVertical: Spacing.large,
}
const label = {
fontSize: sizes.small,
textTransform: 'uppercase'
textTransform: 'uppercase',
}
export default {
accentOrange: {
...accentTextSmall,
color: Colors.orange
color: Colors.orange,
},
accentPurple: {
...accentTextSmall,
color: Colors.purple,
},
accentPurpleBig: {
...accentTextBig,
color: Colors.purple
color: Colors.purple,
},
accentPurpleGiant: {
...accentTextGiant,
color: Colors.purple
color: Colors.purple,
},
accentPurpleHuge: {
...accentTextHuge,
color: Colors.purple
color: Colors.purple,
},
mainText: {
fontFamily: fonts.main,
fontSize: sizes.base
fontSize: sizes.base,
},
label: {
...label
...label,
},
labelBold: {
color: Colors.greyDark,
fontWeight: 'bold',
...label
...label,
},
labelLight: {
color: Colors.grey,
@@ -91,7 +95,7 @@ export default {
},
subtitle: {
fontSize: sizes.subtitle,
...title
...title,
},
title: {
alignSelf: 'center',
@@ -99,7 +103,7 @@ export default {
fontWeight: '700',
fontSize: sizes.title,
marginHorizontal: Spacing.base,
...title
...title,
},
titleWithoutMargin: {
alignSelf: 'center',
@@ -107,5 +111,5 @@ export default {
fontFamily: fonts.bold,
fontWeight: '700',
fontSize: sizes.title,
}
},
}
+30
View File
@@ -0,0 +1,30 @@
import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react-native'
import AcceptLicense from '../../components/AcceptLicense'
import { saveLicenseFlag } from '../../local-storage'
jest.mock('../../local-storage', () => ({
saveLicenseFlag: jest.fn(() => Promise.resolve()),
}))
describe('AcceptLicense', () => {
test('On clicking OK button, the license is accepted', async () => {
const mockedSetLicense = jest.fn()
render(<AcceptLicense setLicense={mockedSetLicense} />)
const okButton = screen.getByText('ok', { exact: false })
fireEvent(okButton, 'click')
await expect(saveLicenseFlag).toHaveBeenCalled()
expect(mockedSetLicense).toHaveBeenCalled()
})
test('There is a Cancel button', async () => {
render(<AcceptLicense setLicense={jest.fn()} />)
screen.getByText('cancel', { exact: false })
})
})
@@ -0,0 +1,395 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Stats screen when provided data, renders stats 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<View
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "space-between",
}
}
>
<View
style={
Object {
"flex": 3,
"flexDirection": "column",
}
}
>
<View
accessibilityIgnoresInvertColors={true}
style={
Object {
"marginBottom": 42.857142857142854,
"paddingTop": 107.14285714285714,
}
}
>
<Image
source={123}
style={
Array [
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
Object {
"height": undefined,
"width": undefined,
},
Object {
"resizeMode": "contain",
},
]
}
/>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 85.71428571428571,
"marginTop": -68.57142857142857,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
30.33
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 68.57142857142857,
"marginTop": -34.285714285714285,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
days
</Text>
</View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
average
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "column",
"paddingTop": 21.428571428571427,
}
}
>
<StatsOverview
data={
Array [
Array [
30,
"min",
],
Array [
31,
"max",
],
Array [
0.58,
"standard_deviation",
],
Array [
3,
"completed_cycles",
],
]
}
/>
</View>
</View>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignItems": "center",
"alignSelf": "center",
"backgroundColor": "#F38337",
"borderRadius": 25,
"flexDirection": "row",
"justifyContent": "center",
"marginTop": 34.285714285714285,
"minWidth": "15%",
"opacity": 1,
"paddingHorizontal": 8.571428571428571,
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Array [
Object {
"color": "white",
"fontFamily": "Jost-Bold",
},
Object {
"fontSize": 27.857142857142858,
"padding": 21.428571428571427,
"textTransform": "uppercase",
},
],
]
}
>
show_stats
</Text>
</View>
<AppHelp
text="standard_deviation_help"
/>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided no data, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided null, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided undefined, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
+13
View File
@@ -0,0 +1,13 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import AppHelp from '../../../components/common/AppHelp'
describe('AppHelp screen', () => {
test('when provided text, should render it', async () => {
const text = 'Some help test'
const { toJSON } = render(<AppHelp text={text} />)
expect(toJSON()).toMatchSnapshot()
})
})
@@ -0,0 +1,25 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import StatsOverview from '../../../components/common/StatsOverview'
describe('StatsOverview screen', () => {
test('when provided correct, renders it', async () => {
const data = [
[21, 'shortest'],
[21, 'longest'],
[0, 'standard deviation'],
[2, 'completed cycles'],
]
const { toJSON } = render(<StatsOverview data={data} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided empty data, renders nothing (does not break)', async () => {
const data = []
const { toJSON } = render(<StatsOverview data={data} />)
expect(toJSON()).toMatchSnapshot()
})
})
+47
View File
@@ -0,0 +1,47 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import StatsTable from '../../../components/common/StatsTable'
const mockGetStats = jest
.fn()
.mockImplementationOnce(() => [
{ date: '2022-07-01', cycleLength: 31, bleedingLength: 5 },
{ date: '2022-06-01', cycleLength: 31, bleedingLength: 5 },
])
.mockImplementationOnce(() => [])
.mockImplementationOnce(() => null)
.mockImplementationOnce(() => undefined)
jest.mock('../../../lib/cycle', () => ({
__esModule: true,
default: () => ({
getStats: mockGetStats,
}),
}))
describe('StatsTable screen', () => {
test('when provided correct data set, renders it', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided no data, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided null, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided undefined, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
})
@@ -0,0 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AppHelp screen when provided text, should render it 1`] = `
<View
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "space-between",
"padding": 34.285714285714285,
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"alignSelf": "flex-start",
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingRight": 34.285714285714285,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
*
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
Some help test
</Text>
</View>
`;
@@ -0,0 +1,321 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StatsOverview screen when provided correct, renders it 1`] = `
Array [
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
21
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
shortest
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
21
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
longest
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
0
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
standard deviation
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
*
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
2
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
completed cycles
</Text>
</View>
</View>,
]
`;
exports[`StatsOverview screen when provided empty data, renders nothing (does not break) 1`] = `null`;
@@ -0,0 +1,433 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StatsTable screen when provided correct data set, renders it 1`] = `
<View
style={
Object {
"alignSelf": "center",
"backgroundColor": "#E9F2ED",
"marginTop": 137.14285714285714,
"maxHeight": 933.8,
"minHeight": "40%",
"position": "absolute",
"width": "100%",
}
}
>
<View
style={
Object {
"flexDirection": "row",
"justifyContent": "flex-end",
"paddingRight": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
hitSlop={
Object {
"bottom": 39.23529411764706,
"left": 42.857142857142854,
"right": 42.857142857142854,
"top": 39.23529411764706,
}
}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignSelf": "flex-start",
"marginBottom": 34.285714285714285,
"opacity": 1,
}
}
>
<Text
allowFontScaling={false}
selectable={false}
style={
Array [
Object {
"color": undefined,
"fontSize": 12,
},
Array [
Object {
"fontSize": 47.14285714285714,
},
undefined,
Object {
"color": "#F38337",
},
],
Object {
"fontFamily": "Entypo",
"fontStyle": "normal",
"fontWeight": "normal",
},
Object {},
]
}
>
</Text>
</View>
</View>
<RCTScrollView
ItemSeparatorComponent={[Function]}
ListHeaderComponent={[Function]}
ListHeaderComponentStyle={
Object {
"borderBottomColor": "#3A2671",
"borderBottomWidth": 2,
}
}
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
}
}
data={
Array [
Object {
"bleedingLength": 5,
"cycleLength": 31,
"date": "2022-07-01",
},
Object {
"bleedingLength": 5,
"cycleLength": 31,
"date": "2022-06-01",
},
]
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
removeClippedSubviews={false}
renderItem={[Function]}
scrollEventThrottle={50}
stickyHeaderIndices={
Array [
0,
]
}
viewabilityConfigCallbackPairs={Array []}
>
<View>
<View
onLayout={[Function]}
style={
Object {
"borderBottomColor": "#3A2671",
"borderBottomWidth": 2,
}
}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
cycle_start
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
cycle_length
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
bleeding
</Text>
</View>
</View>
</View>
<View
onLayout={[Function]}
style={null}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
01. Jul 22
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":31}
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":5}
</Text>
</View>
</View>
<View
style={
Object {
"backgroundColor": "#888",
"height": 1,
"width": "100%",
}
}
/>
</View>
<View
onLayout={[Function]}
style={null}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
01. Jun 22
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":31}
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":5}
</Text>
</View>
</View>
</View>
</View>
</RCTScrollView>
</View>
`;
exports[`StatsTable screen when provided no data, renders nothing 1`] = `null`;
exports[`StatsTable screen when provided null, renders nothing 1`] = `null`;
exports[`StatsTable screen when provided undefined, renders nothing 1`] = `null`;
+19
View File
@@ -0,0 +1,19 @@
import React from 'react'
import { render, screen } from '@testing-library/react-native'
import License from '../../../components/settings/License'
describe('License screen', () => {
test('It should have a correct year', async () => {
render(<License />)
const year = new Date().getFullYear().toString()
screen.getByText(year, { exact: false })
})
test('It should match the snapshot', async () => {
const licenseScreen = render(<License />)
expect(licenseScreen).toMatchSnapshot()
})
})
@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`License screen It should match the snapshot 1`] = `
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Array [
Object {
"backgroundColor": "#E9F2ED",
"flexGrow": 1,
},
undefined,
]
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"alignSelf": "center",
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 51.42857142857143,
"fontWeight": "700",
"marginHorizontal": 34.285714285714285,
"marginVertical": 42.857142857142854,
},
]
}
>
title
</Text>
<View
style={
Object {
"marginBottom": 34.285714285714285,
"marginHorizontal": 34.285714285714285,
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
text{"currentYear":2022}
</Text>
<Text
onPress={[Function]}
style={
Object {
"color": "#3A2671",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
"textDecorationLine": "underline",
}
}
>
https://www.gnu.org/licenses/gpl-3.0.html
</Text>
</View>
</View>
</RCTScrollView>
</View>
`;
+58
View File
@@ -0,0 +1,58 @@
import React from 'react'
import { fireEvent, render } from '@testing-library/react-native'
import Stats from '../../components/stats'
jest.mock('../../components/common/AppHelp', () => 'AppHelp')
jest.mock('../../components/common/StatsOverview', () => 'StatsOverview')
jest.mock('../../components/common/StatsTable', () => 'StatsTable')
const mockGetAllCycleLengths = jest
.fn()
.mockImplementationOnce(() => [])
.mockImplementationOnce(() => [30, 31, 30])
.mockImplementationOnce(() => null)
.mockImplementationOnce(() => undefined)
.mockImplementationOnce(() => [30, 31, 30])
jest.mock('../../lib/cycle', () => ({
__esModule: true,
default: () => ({
getAllCycleLengths: mockGetAllCycleLengths,
}),
}))
describe('Stats screen', () => {
test('when provided no data, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided data, renders stats', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided null, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided undefined, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when button is clicked, StatsTable is rendered', async () => {
const { getByText, findByTestId } = render(<Stats />)
const button = getByText('show_stats')
fireEvent(button, 'click')
await expect(findByTestId('statsTable')).toBeTruthy()
})
})
+1
View File
@@ -0,0 +1 @@
module.exports = 123
+27
View File
@@ -0,0 +1,27 @@
import { humanizeDate } from '../../components/helpers/format-date'
describe('humanizeDate', () => {
test('if receives null, returns empty string', () => {
const result = humanizeDate(null)
expect(result).toEqual('')
})
test('if receives undefined, returns empty string', () => {
const result = humanizeDate(undefined)
expect(result).toEqual('')
})
test('if receives incorrectly formatted date, returns empty string', () => {
const result = humanizeDate('abc')
expect(result).toEqual('')
})
test('if receives correct date string, returns date in humanized format', () => {
const result = humanizeDate('2022-01-07')
expect(result).toEqual('07. Jan 22')
})
})
+8
View File
@@ -0,0 +1,8 @@
// Import Jest Native matchers
import '@testing-library/jest-native/extend-expect'
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (str, options) => str + (options ? JSON.stringify(options) : ''),
}),
}))
+10
View File
@@ -0,0 +1,10 @@
import * as React from 'react'
import { render, screen } from '@testing-library/react-native'
import Logo from '../components/header/logo'
describe('Logo', () => {
test('It works', async () => {
render(<Logo />)
screen.getByText('drip.')
})
})
+160 -71
View File
@@ -102,12 +102,12 @@
json5 "^2.2.1"
semver "^6.3.0"
"@babel/eslint-parser@^7.16.3":
version "7.16.3"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.16.3.tgz#2a6b1702f3f5aea48e00cea5a5bcc241c437e459"
integrity sha512-iB4ElZT0jAt7PKVaeVulOECdGe6UnmA/O0P9jlF5g5GBOwDVbna8AXhHRu4s27xQf6OkveyA8iTDv1jHdDejgQ==
"@babel/eslint-parser@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4"
integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==
dependencies:
eslint-scope "^5.1.1"
"@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
eslint-visitor-keys "^2.1.0"
semver "^6.3.0"
@@ -1379,6 +1379,13 @@
dependencies:
"@sinclair/typebox" "^0.24.1"
"@jest/schemas@^29.0.0":
version "29.0.0"
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a"
integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==
dependencies:
"@sinclair/typebox" "^0.24.1"
"@jest/source-map@^28.1.2":
version "28.1.2"
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24"
@@ -1508,6 +1515,13 @@
resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-5.3.0.tgz#b6f01fa510c9d360134a77f7a2820bdca3a396bf"
integrity sha512-3uObVJ08i0vSbtsTWQ8omy8XUlVDnoest5MOLp6delLUZev8bu++S+3Aua7xWPPWzQt9pcuwDqjEOKslQVDj8g==
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
dependencies:
eslint-scope "5.1.1"
"@ptomasroos/react-native-multi-slider@^2.2.0":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@ptomasroos/react-native-multi-slider/-/react-native-multi-slider-2.2.2.tgz#35a97fb8c355627c6a2ded010b360ac5728b44ad"
@@ -1685,15 +1699,15 @@
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ==
"@react-native/normalize-color@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6"
integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg==
"@react-native/normalize-color@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.0.0.tgz#da955909432474a9a0fe1cbffc66576a0447f567"
integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw==
"@react-native/polyfills@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780"
integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w==
"@react-native/polyfills@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa"
integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==
"@realm.io/common@^0.1.1":
version "0.1.1"
@@ -1736,6 +1750,24 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@testing-library/jest-native@^4.0.12":
version "4.0.12"
resolved "https://registry.yarnpkg.com/@testing-library/jest-native/-/jest-native-4.0.12.tgz#9669a2456bf8f7ac907fca879d157fd0f29e6cb8"
integrity sha512-SjH3mLpYPLt14F2av98172nbGHrOlThKWxbSQrc9ZOsgl8mlMvWkQnFEheQooiLpZwrkoi+P48+dDMU7VaRR3A==
dependencies:
chalk "^4.1.2"
jest-diff "^29.0.1"
jest-matcher-utils "^29.0.1"
pretty-format "^29.0.1"
redent "^3.0.0"
"@testing-library/react-native@^11.1.0":
version "11.1.0"
resolved "https://registry.yarnpkg.com/@testing-library/react-native/-/react-native-11.1.0.tgz#50aaa9c026e4beb02c07607fb0db5f4478cdd625"
integrity sha512-syVlE9fM0tZF4MmEE09R4BoGRH6lMNAbozuluGjS2HT8rXt3unRXb8GwpLmT+xCTq9+1lnQiXobTJm8/w12Zbg==
dependencies:
pretty-format "^29.0.3"
"@types/babel__core@^7.1.14":
version "7.1.19"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460"
@@ -3136,6 +3168,11 @@ diff-sequences@^28.1.1:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6"
integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==
diff-sequences@^29.0.0:
version "29.0.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f"
integrity sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@@ -3374,7 +3411,7 @@ eslint-plugin-react@^7.8.2:
resolve "^2.0.0-next.3"
string.prototype.matchall "^4.0.5"
eslint-scope@^5.1.1:
eslint-scope@5.1.1, eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -4098,10 +4135,10 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hermes-engine@~0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.8.1.tgz#b6d0d70508ac5add2d198304502fb968cdecb8b2"
integrity sha512-as9Iccj/qrqqtDmfYUHbOIjt5xsQbUB6pjNIW3i1+RVr+pCAdz5S8/Jry778mz3rJWplYzHWdR1u1xQSYfBRYw==
hermes-engine@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.9.0.tgz#84d9cfe84e8f6b1b2020d6e71b350cec84ed982f"
integrity sha512-r7U+Y4P2Qg/igFVZN+DpT7JFfXUn1MM4dFne8aW+cCrF6RRymof+VqrUHs1kl07j8h8V2CNesU19RKgWbr3qPw==
hermes-parser@0.4.7:
version "0.4.7"
@@ -4259,6 +4296,11 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -4806,6 +4848,16 @@ jest-diff@^28.1.3:
jest-get-type "^28.0.2"
pretty-format "^28.1.3"
jest-diff@^29.0.1, jest-diff@^29.0.3:
version "29.0.3"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.0.3.tgz#41cc02409ad1458ae1bf7684129a3da2856341ac"
integrity sha512-+X/AIF5G/vX9fWK+Db9bi9BQas7M9oBME7egU7psbn4jlszLFCu0dW63UgeE6cs/GANq4fLaT+8sGHQQ0eCUfg==
dependencies:
chalk "^4.0.0"
diff-sequences "^29.0.0"
jest-get-type "^29.0.0"
pretty-format "^29.0.3"
jest-docblock@^28.1.1:
version "28.1.1"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8"
@@ -4846,6 +4898,11 @@ jest-get-type@^28.0.2:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203"
integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==
jest-get-type@^29.0.0:
version "29.0.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.0.0.tgz#843f6c50a1b778f7325df1129a0fd7aa713aef80"
integrity sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==
jest-haste-map@^26.5.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa"
@@ -4904,6 +4961,16 @@ jest-matcher-utils@^28.1.3:
jest-get-type "^28.0.2"
pretty-format "^28.1.3"
jest-matcher-utils@^29.0.1:
version "29.0.3"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.0.3.tgz#b8305fd3f9e27cdbc210b21fc7dbba92d4e54560"
integrity sha512-RsR1+cZ6p1hDV4GSCQTg+9qjeotQCgkaleIKLK7dm+U4V/H2bWedU3RAtLm8+mANzZ7eDV33dMar4pejd7047w==
dependencies:
chalk "^4.0.0"
jest-diff "^29.0.3"
jest-get-type "^29.0.0"
pretty-format "^29.0.3"
jest-message-util@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d"
@@ -5369,13 +5436,6 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
linkify-it@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -5520,11 +5580,6 @@ mdast-util-compact@^1.0.0:
dependencies:
unist-util-visit "^1.1.0"
mdurl@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
memoize-one@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
@@ -5623,7 +5678,7 @@ metro-minify-uglify@0.66.2:
dependencies:
uglify-es "^3.1.9"
metro-react-native-babel-preset@0.66.2, metro-react-native-babel-preset@^0.66.0:
metro-react-native-babel-preset@0.66.2, metro-react-native-babel-preset@^0.66.2:
version "0.66.2"
resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.66.2.tgz#fddebcf413ad4ea617d4f47f7c1da401052de734"
integrity sha512-H/nLBAz0MgfDloSe1FjyH4EnbokHFdncyERvLPXDACY3ROVRCeUyFNo70ywRGXW2NMbrV4H7KUyU4zkfWhC2HQ==
@@ -5884,6 +5939,11 @@ mimic-response@^3.1.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
minimatch@^3.0.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -6645,6 +6705,15 @@ pretty-format@^28.1.3:
ansi-styles "^5.0.0"
react-is "^18.0.0"
pretty-format@^29.0.1, pretty-format@^29.0.3:
version "29.0.3"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.0.3.tgz#23d5f8cabc9cbf209a77d49409d093d61166a811"
integrity sha512-cHudsvQr1K5vNVLbvYF/nv3Qy/F/BcEKxGuIeMiVMRHxPOO1RxXooP8g/ZrwAp7Dx+KdMZoOc7NxLHhMrP2f9Q==
dependencies:
"@jest/schemas" "^29.0.0"
ansi-styles "^5.0.0"
react-is "^18.0.0"
pretty-quick@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.2.tgz#89d8741af7122cbd7f34182df746c5a7ea360b5c"
@@ -6758,10 +6827,10 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-devtools-core@^4.6.0:
version "4.25.0"
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.25.0.tgz#78b11a2c9f81dd9ebff3745ab4ee2147cc96c12a"
integrity sha512-iewRrnu0ZnmfL+jJayKphXj04CFh6i3ezVnpCtcnZbTPSQgN09XqHAzXbKbqNDl7aTg9QLNkQRP6M3DvdrinWA==
react-devtools-core@4.19.1:
version "4.19.1"
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.19.1.tgz#bc37c2ef2f48f28c6af4c7292be9dca1b63deace"
integrity sha512-2wJiGffPWK0KggBjVwnTaAk+Z3MSxKInHmdzPTrBh1mAarexsa93Kw+WMX88+XjN+TtYgAiLe9xeTqcO5FfJTw==
dependencies:
shell-quote "^1.6.1"
ws "^7"
@@ -6774,21 +6843,21 @@ react-i18next@^11.18.3:
"@babel/runtime" "^7.14.5"
html-parse-stringify "^3.0.1"
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.1:
react-is@^17.0.1, react-is@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-native-calendars@^1.1287.0:
version "1.1287.0"
resolved "https://registry.yarnpkg.com/react-native-calendars/-/react-native-calendars-1.1287.0.tgz#0abfbe55269f98d2186357f5dc6eb1edbeca80e7"
@@ -6805,10 +6874,10 @@ react-native-calendars@^1.1287.0:
optionalDependencies:
moment "^2.24.0"
react-native-codegen@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.7.tgz#86651c5c5fec67a8077ef7f4e36f7ed459043e14"
integrity sha512-dwNgR8zJ3ALr480QnAmpTiqvFo+rDtq6V5oCggKhYFlRjzOmVSFn3YD41u8ltvKS5G2nQ8gCs2vReFFnRGLYng==
react-native-codegen@^0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.8.tgz#b7796a54074139d956fff2862cf1285db43c891b"
integrity sha512-k/944+0XD+8l7zDaiKfYabyEKmAmyZgS1mj+4LcSRPyHnrjgCHKrh/Y6jM6kucQ6xU1+1uyMmF/dSkikxK8i+Q==
dependencies:
flow-parser "^0.121.0"
jscodeshift "^0.11.0"
@@ -6829,18 +6898,10 @@ react-native-fs@^2.20.0:
base-64 "^0.1.0"
utf8 "^3.0.0"
react-native-hyperlink@0.0.19:
version "0.0.19"
resolved "https://registry.yarnpkg.com/react-native-hyperlink/-/react-native-hyperlink-0.0.19.tgz#ca375cde1d244bb94c551736852ee0d688c8bb1f"
integrity sha512-x4wuRGDMnnpWcRr5MCK1D2UcEuzD9IHK8lfjEhO/+QqXNaX31HdeD3ss3BXXZgHxpRYtLbTB0TuFcl1HHANp3w==
dependencies:
linkify-it "^2.2.0"
mdurl "^1.0.0"
react-native-modal-datetime-picker@10.2.0:
version "10.2.0"
resolved "https://registry.yarnpkg.com/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-10.2.0.tgz#3ec5c299e8bdc9fd2ec0a1a6642a459c408b910f"
integrity sha512-eMQb3EPqHx47WrlLPTrXvmLZDwGwGH//WTWiQBUvJ+6ehDeuTjIn8/v/ANv8wxCCrt4NeHem8FQY3Z5fa4fRgQ==
react-native-modal-datetime-picker@14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-14.0.0.tgz#ca2c81a275ee3a23d9ad02113e76ed243c90781e"
integrity sha512-orI0BMgX9uooSZWYIILmMaZXqi8Ebr0vqsLUFL03zORpv9NvkBlLMQ8dZVdDxWUc1Lbx/N5DJskC16AOypPy8Q==
dependencies:
prop-types "^15.7.2"
@@ -6898,26 +6959,25 @@ react-native-version@^3.1.0:
resolve-from "^5.0.0"
semver "^6.0.0"
react-native@0.65.2:
version "0.65.2"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.65.2.tgz#5d580ffe7b64c4966e6178a910aeefd3b40d4fdb"
integrity sha512-0pzZFmSv38691PB2Zj2JE3x+57a8vdgGeUw2ai0MR3p6psdRes3BBR8miEv/gB7fgFKNDKdRJGDuyFVioKN1jA==
react-native@0.67.4:
version "0.67.4"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.67.4.tgz#57a1503daa81f66a61f521ff67b259f126d87f9c"
integrity sha512-NA9d9lNJu9TViEJu2uZxWXUP+QNUilGGA5tdMbVFedNroOH1lnQ3n/FAVoGK1gqGarCgNTtheBxUpEa979Cu8w==
dependencies:
"@jest/create-cache-key-function" "^27.0.1"
"@react-native-community/cli" "^6.0.0"
"@react-native-community/cli-platform-android" "^6.0.0"
"@react-native-community/cli-platform-ios" "^6.0.0"
"@react-native/assets" "1.0.0"
"@react-native/normalize-color" "1.0.0"
"@react-native/polyfills" "1.0.0"
"@react-native/normalize-color" "2.0.0"
"@react-native/polyfills" "2.0.0"
abort-controller "^3.0.0"
anser "^1.4.9"
base64-js "^1.1.2"
event-target-shim "^5.0.1"
hermes-engine "~0.8.1"
hermes-engine "~0.9.0"
invariant "^2.2.4"
jsc-android "^250230.2.1"
metro-babel-register "0.66.2"
metro-react-native-babel-transformer "0.66.2"
metro-runtime "0.66.2"
metro-source-map "0.66.2"
@@ -6925,7 +6985,8 @@ react-native@0.65.2:
pretty-format "^26.5.2"
promise "^8.0.3"
prop-types "^15.7.2"
react-devtools-core "^4.6.0"
react-devtools-core "4.19.1"
react-native-codegen "^0.0.8"
react-refresh "^0.4.0"
regenerator-runtime "^0.13.2"
scheduler "^0.20.2"
@@ -6939,6 +7000,24 @@ react-refresh@^0.4.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"
integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==
react-shallow-renderer@^16.13.1:
version "16.15.0"
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
dependencies:
object-assign "^4.1.1"
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
react-test-renderer@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
dependencies:
object-assign "^4.1.1"
react-is "^17.0.2"
react-shallow-renderer "^16.13.1"
scheduler "^0.20.2"
react@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
@@ -7028,6 +7107,14 @@ recyclerlistview@^3.0.5:
prop-types "15.5.8"
ts-object-utils "0.0.5"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
dependencies:
indent-string "^4.0.0"
strip-indent "^3.0.0"
regenerate-unicode-properties@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
@@ -7852,6 +7939,13 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
dependencies:
min-indent "^1.0.0"
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -8173,11 +8267,6 @@ ua-parser-js@^0.7.30:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
uc.micro@^1.0.1:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uglify-es@^3.1.9:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"