Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bee6b145ed | |||
| 57c8e31a9a | |||
| 726b65914b | |||
| 82b6a6b603 | |||
| 5f61f37d2f | |||
| 1b6d21f730 | |||
| be0c11abfe | |||
| e2e320927b | |||
| 5c2a80c5c2 | |||
| 2e060d3261 | |||
| c267e80424 | |||
| c95e25a9b2 | |||
| 6c1ee3e5e2 | |||
| 499e2d0628 | |||
| 951fb778d4 | |||
| 6d6473ca78 | |||
| 5a0321c5e5 | |||
| 0c6c706274 | |||
| 8774d251de | |||
| 974d081f40 | |||
| c847270159 | |||
| d58c230eda | |||
| 0747ea8a5f | |||
| 0bba7afc6f | |||
| f957553026 | |||
| 0597540b88 | |||
| 75823ed750 | |||
| dd1c2cd96d | |||
| 06d346ee46 | |||
| d4bd576cc9 | |||
| 85b3a8b4b6 | |||
| 206d4b06fa | |||
| 2669738c9d | |||
| 195c792837 | |||
| f6a90994b6 | |||
| fb863c832b | |||
| 7dd1a297a2 | |||
| 1eadd1c5d6 | |||
| 3c7cb3dfff | |||
| ef3a50ea76 | |||
| 5166b65e81 | |||
| 658ce1d074 | |||
| 997332565f | |||
| f1df68e973 | |||
| e289094d97 | |||
| 2fb82d95e6 | |||
| 1610c8416a | |||
| 971ea4bdcf |
@@ -0,0 +1,2 @@
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
||||
@@ -32,6 +32,7 @@ build/
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
|
||||
# node.js
|
||||
#
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
2.7.4
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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]),
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')}>
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -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`;
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = 123
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
@@ -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) : ''),
|
||||
}),
|
||||
}))
|
||||
@@ -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.')
|
||||
})
|
||||
})
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user