Merge branch 'master' into '129-extract-text-from-stats-and-layout'
# Conflicts: # components/labels.js
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:react/recommended"
|
"plugin:react/recommended"
|
||||||
],
|
],
|
||||||
|
"parser": "babel-eslint",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ project.ext.react = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
apply from: "../../node_modules/react-native/react.gradle"
|
apply from: "../../node_modules/react-native/react.gradle"
|
||||||
|
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this to true to create two separate APKs instead of one:
|
* Set this to true to create two separate APKs instead of one:
|
||||||
@@ -137,9 +138,11 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile project(':react-native-vector-icons')
|
||||||
|
compile project(':react-native-fs')
|
||||||
|
compile project(':react-native-document-picker')
|
||||||
compile project(':react-native-share')
|
compile project(':react-native-share')
|
||||||
compile project(':realm')
|
compile project(':realm')
|
||||||
compile project(':react-native-svg')
|
|
||||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||||
compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||||
compile "com.facebook.react:react-native:+" // From node_modules
|
compile "com.facebook.react:react-native:+" // From node_modules
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,10 +3,12 @@ package com.drip;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import com.facebook.react.ReactApplication;
|
import com.facebook.react.ReactApplication;
|
||||||
|
import com.oblador.vectoricons.VectorIconsPackage;
|
||||||
|
import com.rnfs.RNFSPackage;
|
||||||
|
import com.reactnativedocumentpicker.ReactNativeDocumentPicker;
|
||||||
import cl.json.RNSharePackage;
|
import cl.json.RNSharePackage;
|
||||||
import cl.json.ShareApplication;
|
import cl.json.ShareApplication;
|
||||||
import io.realm.react.RealmReactPackage;
|
import io.realm.react.RealmReactPackage;
|
||||||
import com.horcrux.svg.SvgPackage;
|
|
||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
@@ -27,9 +29,11 @@ public class MainApplication extends Application implements ReactApplication, Sh
|
|||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
return Arrays.<ReactPackage>asList(
|
return Arrays.<ReactPackage>asList(
|
||||||
new MainReactPackage(),
|
new MainReactPackage(),
|
||||||
|
new VectorIconsPackage(),
|
||||||
|
new RNFSPackage(),
|
||||||
|
new ReactNativeDocumentPicker(),
|
||||||
new RNSharePackage(),
|
new RNSharePackage(),
|
||||||
new RealmReactPackage(),
|
new RealmReactPackage()
|
||||||
new SvgPackage()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
rootProject.name = 'drip'
|
rootProject.name = 'drip'
|
||||||
|
include ':react-native-vector-icons'
|
||||||
|
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
|
||||||
|
include ':react-native-fs'
|
||||||
|
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
|
||||||
|
include ':react-native-document-picker'
|
||||||
|
project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android')
|
||||||
include ':react-native-share'
|
include ':react-native-share'
|
||||||
project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android')
|
project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android')
|
||||||
include ':realm'
|
include ':realm'
|
||||||
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
||||||
include ':react-native-svg'
|
|
||||||
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createStackNavigator } from 'react-navigation'
|
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation'
|
||||||
import Home from './components/home'
|
import Home from './components/home'
|
||||||
|
|
||||||
import Calendar from './components/calendar'
|
import Calendar from './components/calendar'
|
||||||
@@ -7,16 +7,28 @@ import Chart from './components/chart/chart'
|
|||||||
import Settings from './components/settings'
|
import Settings from './components/settings'
|
||||||
import Stats from './components/stats'
|
import Stats from './components/stats'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
// this is until react native fixes this bugg, see
|
// this is until react native fixes this bugg, see
|
||||||
// https://github.com/facebook/react-native/issues/18868#issuecomment-382671739
|
// https://github.com/facebook/react-native/issues/18868#issuecomment-382671739
|
||||||
import { YellowBox } from 'react-native'
|
import { YellowBox } from 'react-native'
|
||||||
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated'])
|
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated'])
|
||||||
|
|
||||||
export default createStackNavigator({
|
const routes = {
|
||||||
home: { screen: Home },
|
Home: createStackNavigator({Home, CycleDay}, {headerMode: 'none'}),
|
||||||
calendar: { screen: Calendar },
|
Calendar: createStackNavigator({Calendar, CycleDay}, {headerMode: 'none'}),
|
||||||
cycleDay: { screen: CycleDay },
|
Chart: createStackNavigator({Chart, CycleDay}, {headerMode: 'none'}),
|
||||||
chart: { screen: Chart },
|
Settings: { screen: Settings },
|
||||||
settings: { screen: Settings },
|
Stats: { screen: Stats}
|
||||||
stats: { screen: Stats}
|
}
|
||||||
})
|
|
||||||
|
const config = {
|
||||||
|
labeled: true,
|
||||||
|
shifting: false,
|
||||||
|
tabBarOptions: {
|
||||||
|
style: {backgroundColor: '#ff7e5f'},
|
||||||
|
labelStyle: {fontSize: 15, color: 'white'}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createBottomTabNavigator(routes, config)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Calendar } from 'react-native-calendars'
|
import { CalendarList } from 'react-native-calendars'
|
||||||
import * as styles from '../styles'
|
import * as styles from '../styles'
|
||||||
import { getOrCreateCycleDay, bleedingDaysSortedByDate } from '../db'
|
import { getOrCreateCycleDay, bleedingDaysSortedByDate } from '../db'
|
||||||
|
|
||||||
@@ -29,13 +29,13 @@ export default class CalendarView extends Component {
|
|||||||
passDateToDayView(result) {
|
passDateToDayView(result) {
|
||||||
const cycleDay = getOrCreateCycleDay(result.dateString)
|
const cycleDay = getOrCreateCycleDay(result.dateString)
|
||||||
const navigate = this.props.navigation.navigate
|
const navigate = this.props.navigation.navigate
|
||||||
navigate('cycleDay', { cycleDay })
|
navigate('CycleDay', { cycleDay })
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Calendar
|
<CalendarList
|
||||||
onDayPress={ this.passDateToDayView.bind(this) }
|
onDayPress={ this.passDateToDayView.bind(this) }
|
||||||
markedDates = { this.state.bleedingDaysInCalFormat }
|
markedDates = { this.state.bleedingDaysInCalFormat }
|
||||||
markingType = {'period'}
|
markingType = {'period'}
|
||||||
|
|||||||
+67
-271
@@ -1,35 +1,34 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { Text as ReactNativeText, View, FlatList, ScrollView } from 'react-native'
|
import { View, FlatList } from 'react-native'
|
||||||
import range from 'date-range'
|
import range from 'date-range'
|
||||||
import Svg,{
|
|
||||||
G,
|
|
||||||
Rect,
|
|
||||||
Text,
|
|
||||||
Circle,
|
|
||||||
Line,
|
|
||||||
Path
|
|
||||||
} from 'react-native-svg'
|
|
||||||
import { LocalDate } from 'js-joda'
|
import { LocalDate } from 'js-joda'
|
||||||
import { getCycleDay, getOrCreateCycleDay, cycleDaysSortedByDate } from '../../db'
|
import { yAxis, normalizeToScale, horizontalGrid } from './y-axis'
|
||||||
import cycleModule from '../../lib/cycle'
|
import setUpFertilityStatusFunc from './nfp-lines'
|
||||||
|
import DayColumn from './day-column'
|
||||||
|
import { getCycleDay, cycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
|
||||||
import styles from './styles'
|
import styles from './styles'
|
||||||
import config from './config'
|
|
||||||
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
|
||||||
|
|
||||||
const getCycleDayNumber = cycleModule().getCycleDayNumber
|
const yAxisView = <View {...styles.yAxis}>{yAxis.labels}</View>
|
||||||
|
|
||||||
const yAxis = makeYAxis(config)
|
|
||||||
|
|
||||||
export default class CycleChart extends Component {
|
export default class CycleChart extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
columns: makeColumnInfo(config.xAxisRangeInDays)
|
columns: makeColumnInfo(setUpFertilityStatusFunc()),
|
||||||
|
}
|
||||||
|
this.renderColumn = ({item, index}) => {
|
||||||
|
return (
|
||||||
|
<DayColumn
|
||||||
|
{...item}
|
||||||
|
index={index}
|
||||||
|
navigate={this.props.navigation.navigate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reCalculateChartInfo = (function(Chart) {
|
this.reCalculateChartInfo = (function(Chart) {
|
||||||
return function() {
|
return function() {
|
||||||
Chart.setState({columns: makeColumnInfo(config.xAxisRangeInDays)})
|
Chart.setState({columns: makeColumnInfo(setUpFertilityStatusFunc())})
|
||||||
}
|
}
|
||||||
})(this)
|
})(this)
|
||||||
|
|
||||||
@@ -40,159 +39,37 @@ export default class CycleChart extends Component {
|
|||||||
cycleDaysSortedByDate.removeListener(this.reCalculateChartInfo)
|
cycleDaysSortedByDate.removeListener(this.reCalculateChartInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
passDateToDayView(dateString) {
|
|
||||||
const cycleDay = getOrCreateCycleDay(dateString)
|
|
||||||
this.props.navigation.navigate('cycleDay', { cycleDay })
|
|
||||||
}
|
|
||||||
|
|
||||||
placeHorizontalGrid() {
|
|
||||||
return yAxis.tickPositions.map(tick => {
|
|
||||||
return (
|
|
||||||
<Line
|
|
||||||
x1={0}
|
|
||||||
y1={tick}
|
|
||||||
x2={config.columnWidth}
|
|
||||||
y2={tick}
|
|
||||||
{...styles.horizontalGrid}
|
|
||||||
key={tick}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
makeDayColumn({ dateString, cycleDay, y }, index) {
|
|
||||||
const cycleDayNumber = getCycleDayNumber(dateString)
|
|
||||||
const label = styles.column.label
|
|
||||||
const dateLabel = dateString.split('-').slice(1).join('-')
|
|
||||||
const getFhmAndLtlInfo = setUpFertilityStatusFunc()
|
|
||||||
const nfpLineInfo = getFhmAndLtlInfo(dateString, cycleDay)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<G onPress={() => this.passDateToDayView(dateString)}>
|
|
||||||
<Rect {...styles.column.rect} />
|
|
||||||
{nfpLineInfo.drawFhmLine ?
|
|
||||||
<Line
|
|
||||||
x1={0 + styles.nfpLine.strokeWidth / 2}
|
|
||||||
y1="20"
|
|
||||||
x2={0 + styles.nfpLine.strokeWidth / 2}
|
|
||||||
y2={config.chartHeight - 20}
|
|
||||||
{...styles.nfpLine}
|
|
||||||
/> : null}
|
|
||||||
|
|
||||||
{this.placeHorizontalGrid()}
|
|
||||||
|
|
||||||
<Text {...label.number} y={config.cycleDayNumberRowY}>
|
|
||||||
{cycleDayNumber}
|
|
||||||
</Text>
|
|
||||||
<Text {...label.date} y={config.dateRowY}>
|
|
||||||
{dateLabel}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{cycleDay && cycleDay.bleeding ?
|
|
||||||
<Path {...styles.bleedingIcon}
|
|
||||||
d="M15 3
|
|
||||||
Q16.5 6.8 25 18
|
|
||||||
A12.8 12.8 0 1 1 5 18
|
|
||||||
Q13.5 6.8 15 3z" />
|
|
||||||
: null}
|
|
||||||
|
|
||||||
{nfpLineInfo.drawLtlAt ?
|
|
||||||
<Line
|
|
||||||
x1="0"
|
|
||||||
y1={nfpLineInfo.drawLtlAt}
|
|
||||||
x2={config.columnWidth}
|
|
||||||
y2={nfpLineInfo.drawLtlAt}
|
|
||||||
{...styles.nfpLine}
|
|
||||||
/> : null}
|
|
||||||
|
|
||||||
{y ?
|
|
||||||
this.drawDotAndLines(y, cycleDay.temperature.exclude, index)
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{cycleDay && cycleDay.mucus ?
|
|
||||||
<Circle
|
|
||||||
{...styles.mucusIcon}
|
|
||||||
fill={styles.mucusIconShades[cycleDay.mucus.value]}
|
|
||||||
/> : null}
|
|
||||||
|
|
||||||
{y ?
|
|
||||||
this.drawDotAndLines(y, cycleDay.temperature.exclude, index)
|
|
||||||
: null}
|
|
||||||
</G>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
drawDotAndLines(currY, exclude, index) {
|
|
||||||
let lineToRight
|
|
||||||
let lineToLeft
|
|
||||||
const cols = this.state.columns
|
|
||||||
|
|
||||||
function makeLine(otherColY, x, excludeLine) {
|
|
||||||
const middleY = ((otherColY - currY) / 2) + currY
|
|
||||||
const target = [x, middleY]
|
|
||||||
const lineStyle = excludeLine ? styles.curveExcluded : styles.curve
|
|
||||||
|
|
||||||
return <Line
|
|
||||||
x1={config.columnMiddle}
|
|
||||||
y1={currY}
|
|
||||||
x2={target[0]}
|
|
||||||
y2={target[1]}
|
|
||||||
{...lineStyle}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
const thereIsADotToTheRight = index > 0 && cols[index - 1].y
|
|
||||||
const thereIsADotToTheLeft = index < cols.length - 1 && cols[index + 1].y
|
|
||||||
|
|
||||||
if (thereIsADotToTheRight) {
|
|
||||||
const otherDot = cols[index - 1]
|
|
||||||
const excludedLine = otherDot.cycleDay.temperature.exclude || exclude
|
|
||||||
lineToRight = makeLine(otherDot.y, config.columnWidth, excludedLine)
|
|
||||||
}
|
|
||||||
if (thereIsADotToTheLeft) {
|
|
||||||
const otherDot = cols[index + 1]
|
|
||||||
const excludedLine = otherDot.cycleDay.temperature.exclude || exclude
|
|
||||||
lineToLeft = makeLine(otherDot.y, 0, excludedLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dotStyle = exclude ? styles.curveDotsExcluded : styles.curveDots
|
|
||||||
return (<G>
|
|
||||||
{lineToRight}
|
|
||||||
{lineToLeft}
|
|
||||||
<Circle
|
|
||||||
cx={config.columnMiddle}
|
|
||||||
cy={currY}
|
|
||||||
{...dotStyle}
|
|
||||||
/>
|
|
||||||
</G>)
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ScrollView contentContainerStyle={{flexDirection: 'row'}}>
|
<View style={{ flexDirection: 'row', marginTop: 50 }}>
|
||||||
<View {...styles.yAxis}>{yAxis.labels}</View>
|
{yAxisView}
|
||||||
<FlatList
|
{horizontalGrid}
|
||||||
|
{<FlatList
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
inverted={true}
|
inverted={true}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
data={this.state.columns}
|
data={this.state.columns}
|
||||||
renderItem={({ item, index }) => {
|
renderItem={this.renderColumn}
|
||||||
return (
|
|
||||||
<Svg width={config.columnWidth} height={config.chartHeight}>
|
|
||||||
{this.makeDayColumn(item, index)}
|
|
||||||
</Svg>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
keyExtractor={item => item.dateString}
|
keyExtractor={item => item.dateString}
|
||||||
|
initialNumToRender={15}
|
||||||
|
maxToRenderPerBatch={5}
|
||||||
>
|
>
|
||||||
</FlatList>
|
</FlatList>}
|
||||||
</ScrollView>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeColumnInfo(n) {
|
function makeColumnInfo(getFhmAndLtlInfo) {
|
||||||
const xAxisDates = getPreviousDays(n).map(jsDate => {
|
let amountOfCycleDays = getAmountOfCycleDays()
|
||||||
|
// if there's not much data yet, we want to show at least 30 days on the chart
|
||||||
|
if (amountOfCycleDays < 30) {
|
||||||
|
amountOfCycleDays = 30
|
||||||
|
} else {
|
||||||
|
// we don't want the chart to end abruptly before the first data day
|
||||||
|
amountOfCycleDays += 5
|
||||||
|
}
|
||||||
|
const xAxisDates = getTodayAndPreviousDays(amountOfCycleDays).map(jsDate => {
|
||||||
return LocalDate.of(
|
return LocalDate.of(
|
||||||
jsDate.getFullYear(),
|
jsDate.getFullYear(),
|
||||||
jsDate.getMonth() + 1,
|
jsDate.getMonth() + 1,
|
||||||
@@ -200,18 +77,29 @@ function makeColumnInfo(n) {
|
|||||||
).toString()
|
).toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
return xAxisDates.map(dateString => {
|
const columns = xAxisDates.map(dateString => {
|
||||||
const cycleDay = getCycleDay(dateString)
|
const cycleDay = getCycleDay(dateString)
|
||||||
const temp = cycleDay && cycleDay.temperature && cycleDay.temperature.value
|
const symptoms = ['temperature', 'mucus', 'bleeding'].reduce((acc, symptom) => {
|
||||||
|
acc[symptom] = cycleDay && cycleDay[symptom] && cycleDay[symptom].value
|
||||||
|
acc[`${symptom}Exclude`] = cycleDay && cycleDay[symptom] && cycleDay[symptom].exclude
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dateString,
|
dateString,
|
||||||
cycleDay,
|
y: symptoms.temperature ? normalizeToScale(symptoms.temperature) : null,
|
||||||
y: temp ? normalizeToScale(temp) : null
|
...symptoms,
|
||||||
|
...getFhmAndLtlInfo(dateString, symptoms.temperature)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return columns.map((col, i) => {
|
||||||
|
const info = getInfoForNeighborColumns(i, columns)
|
||||||
|
return Object.assign(col, info)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPreviousDays(n) {
|
function getTodayAndPreviousDays(n) {
|
||||||
const today = new Date()
|
const today = new Date()
|
||||||
today.setHours(0)
|
today.setHours(0)
|
||||||
today.setMinutes(0)
|
today.setMinutes(0)
|
||||||
@@ -222,114 +110,22 @@ function getPreviousDays(n) {
|
|||||||
return range(earlierDate, today).reverse()
|
return range(earlierDate, today).reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeToScale(temp) {
|
function getInfoForNeighborColumns(index, cols) {
|
||||||
const scale = config.temperatureScale
|
const ret = {
|
||||||
const valueRelativeToScale = (scale.high - temp) / (scale.high - scale.low)
|
rightY: null,
|
||||||
const scaleHeight = config.chartHeight
|
rightTemperatureExclude: null,
|
||||||
return scaleHeight * valueRelativeToScale
|
leftY: null,
|
||||||
}
|
leftTemperatureExclude: null
|
||||||
|
|
||||||
function makeYAxis() {
|
|
||||||
const scaleMin = config.temperatureScale.low
|
|
||||||
const scaleMax = config.temperatureScale.high
|
|
||||||
const numberOfTicks = (scaleMax - scaleMin) * 2
|
|
||||||
const tickDistance = config.chartHeight / numberOfTicks
|
|
||||||
|
|
||||||
const tickPositions = []
|
|
||||||
const labels = []
|
|
||||||
// for style reasons, we don't want the first and last tick
|
|
||||||
for (let i = 1; i < numberOfTicks - 1; i++) {
|
|
||||||
const y = tickDistance * i
|
|
||||||
const style = styles.yAxisLabel
|
|
||||||
// this eyeballing is sadly necessary because RN does not
|
|
||||||
// support percentage values for transforms, which we'd need
|
|
||||||
// to reliably place the label vertically centered to the grid
|
|
||||||
style.top = y - 8
|
|
||||||
labels.push(
|
|
||||||
<ReactNativeText
|
|
||||||
style={{...style}}
|
|
||||||
key={i}>
|
|
||||||
{scaleMax - i * 0.5}
|
|
||||||
</ReactNativeText>
|
|
||||||
)
|
|
||||||
tickPositions.push(y)
|
|
||||||
}
|
}
|
||||||
|
const right = index > 0 ? cols[index - 1] : undefined
|
||||||
return {labels, tickPositions}
|
const left = index < cols.length - 1 ? cols[index + 1] : undefined
|
||||||
}
|
if (right && right.y) {
|
||||||
|
ret.rightY = right.y
|
||||||
function setUpFertilityStatusFunc() {
|
ret.rightTemperatureExclude = right.temperatureExclude
|
||||||
let cycleStatus
|
|
||||||
let cycleStartDate
|
|
||||||
let noMoreCycles = false
|
|
||||||
|
|
||||||
function updateCurrentCycle(dateString) {
|
|
||||||
cycleStatus = getCycleStatusForDay(dateString)
|
|
||||||
if(!cycleStatus) {
|
|
||||||
noMoreCycles = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cycleStatus.phases.preOvulatory) {
|
|
||||||
cycleStartDate = cycleStatus.phases.preOvulatory.start.date
|
|
||||||
} else {
|
|
||||||
cycleStartDate = cycleStatus.phases.periOvulatory.start.date
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (left && left.y) {
|
||||||
function dateIsInPeriOrPostPhase(dateString) {
|
ret.leftY = left.y
|
||||||
return (
|
ret.leftTemperatureExclude = left.temperatureExclude
|
||||||
dateString >= cycleStatus.phases.periOvulatory.start.date
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function precededByAnotherTempValue(dateString) {
|
|
||||||
return (
|
|
||||||
// we are only interested in days that have a preceding
|
|
||||||
// temp
|
|
||||||
Object.keys(cycleStatus.phases).some(phaseName => {
|
|
||||||
return cycleStatus.phases[phaseName].cycleDays.some(day => {
|
|
||||||
return day.temperature && day.date < dateString
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// and also a following temp, so we don't draw the line
|
|
||||||
// longer than necessary
|
|
||||||
&&
|
|
||||||
cycleStatus.phases.postOvulatory.cycleDays.some(day => {
|
|
||||||
return day.temperature && day.date > dateString
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isInTempMeasuringPhase(cycleDay, dateString) {
|
|
||||||
return (
|
|
||||||
cycleDay && cycleDay.temperature
|
|
||||||
|| precededByAnotherTempValue(dateString)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return function(dateString, cycleDay) {
|
|
||||||
const ret = {}
|
|
||||||
if (!cycleStatus && !noMoreCycles) updateCurrentCycle(dateString)
|
|
||||||
if (noMoreCycles) return ret
|
|
||||||
|
|
||||||
if (dateString < cycleStartDate) updateCurrentCycle(dateString)
|
|
||||||
if (noMoreCycles) return ret
|
|
||||||
|
|
||||||
const tempShift = cycleStatus.temperatureShift
|
|
||||||
|
|
||||||
if (tempShift) {
|
|
||||||
if (tempShift.firstHighMeasurementDay.date === dateString) {
|
|
||||||
ret.drawFhmLine = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
dateIsInPeriOrPostPhase(dateString) &&
|
|
||||||
isInTempMeasuringPhase(cycleDay, dateString)
|
|
||||||
) {
|
|
||||||
ret.drawLtlAt = normalizeToScale(tempShift.ltl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
const config = {
|
const config = {
|
||||||
chartHeight: 350,
|
chartHeight: 350,
|
||||||
columnWidth: 30,
|
columnWidth: 25,
|
||||||
temperatureScale: {
|
temperatureScale: {
|
||||||
low: 33,
|
low: 35,
|
||||||
high: 40
|
high: 38,
|
||||||
},
|
units: 0.1
|
||||||
xAxisRangeInDays: 40
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const margin = 3
|
const margin = 3
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
Text, View, TouchableOpacity
|
||||||
|
} from 'react-native'
|
||||||
|
import Icon from 'react-native-vector-icons/Entypo'
|
||||||
|
import styles from './styles'
|
||||||
|
import config from './config'
|
||||||
|
import { getOrCreateCycleDay } from '../../db'
|
||||||
|
import cycleModule from '../../lib/cycle'
|
||||||
|
import DotAndLine from './dot-and-line'
|
||||||
|
|
||||||
|
const getCycleDayNumber = cycleModule().getCycleDayNumber
|
||||||
|
const label = styles.column.label
|
||||||
|
|
||||||
|
export default class DayColumn extends Component {
|
||||||
|
passDateToDayView(dateString) {
|
||||||
|
const cycleDay = getOrCreateCycleDay(dateString)
|
||||||
|
this.props.navigate('cycleDay', { cycleDay })
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(newProps) {
|
||||||
|
return Object.keys(newProps).some(key => newProps[key] != this.props[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
dateString,
|
||||||
|
y,
|
||||||
|
temperatureExclude,
|
||||||
|
bleeding,
|
||||||
|
mucus,
|
||||||
|
drawFhmLine,
|
||||||
|
drawLtlAt,
|
||||||
|
rightY,
|
||||||
|
rightTemperatureExclude,
|
||||||
|
leftY,
|
||||||
|
leftTemperatureExclude
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const columnElements = []
|
||||||
|
if (typeof bleeding === 'number') {
|
||||||
|
columnElements.push(
|
||||||
|
<Icon
|
||||||
|
name='drop'
|
||||||
|
position='absolute'
|
||||||
|
size={18}
|
||||||
|
color='#900'
|
||||||
|
style={{ marginTop: 10, marginLeft: 3 }}
|
||||||
|
key='bleeding'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof mucus === 'number') {
|
||||||
|
const mucusIcon = (
|
||||||
|
<View
|
||||||
|
position='absolute'
|
||||||
|
top = {40}
|
||||||
|
left = {config.columnMiddle - styles.mucusIcon.width / 2}
|
||||||
|
{...styles.mucusIcon}
|
||||||
|
backgroundColor={styles.mucusIconShades[mucus]}
|
||||||
|
key='mucus'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
columnElements.push(mucusIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(drawFhmLine) {
|
||||||
|
const fhmLine = (<View
|
||||||
|
position = 'absolute'
|
||||||
|
top={100}
|
||||||
|
width={styles.nfpLine.strokeWidth}
|
||||||
|
height={200}
|
||||||
|
{...styles.nfpLine}
|
||||||
|
key='fhm'
|
||||||
|
/>)
|
||||||
|
columnElements.push(fhmLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(drawLtlAt) {
|
||||||
|
const ltlLine = (<View
|
||||||
|
position = 'absolute'
|
||||||
|
width={'100%'}
|
||||||
|
top={drawLtlAt}
|
||||||
|
{...styles.nfpLine}
|
||||||
|
key='ltl'
|
||||||
|
/>)
|
||||||
|
columnElements.push(ltlLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y) {
|
||||||
|
columnElements.push(
|
||||||
|
<DotAndLine
|
||||||
|
y={y}
|
||||||
|
exclude={temperatureExclude}
|
||||||
|
rightY={rightY}
|
||||||
|
rightTemperatureExclude={rightTemperatureExclude}
|
||||||
|
leftY={leftY}
|
||||||
|
leftTemperatureExclude={leftTemperatureExclude}
|
||||||
|
key='dotandline'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cycleDayNumber = getCycleDayNumber(dateString)
|
||||||
|
const shortDate = dateString.split('-').slice(1).join('-')
|
||||||
|
const cycleDayLabel = (
|
||||||
|
<Text style={label.number} y={config.cycleDayNumberRowY}>
|
||||||
|
{cycleDayNumber}
|
||||||
|
</Text>)
|
||||||
|
const dateLabel = (
|
||||||
|
<Text style = {label.date} y={config.dateRowY}>
|
||||||
|
{shortDate}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
columnElements.push(
|
||||||
|
<View position='absolute' bottom={0} key='date'>
|
||||||
|
{cycleDayLabel}
|
||||||
|
{dateLabel}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
TouchableOpacity,
|
||||||
|
{
|
||||||
|
style: styles.column.rect,
|
||||||
|
key: this.props.index.toString(),
|
||||||
|
onPress: () => {
|
||||||
|
this.passDateToDayView(dateString)
|
||||||
|
},
|
||||||
|
activeOpacity: 1
|
||||||
|
},
|
||||||
|
columnElements
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import styles from './styles'
|
||||||
|
import config from './config'
|
||||||
|
|
||||||
|
export default class DotAndLine extends Component {
|
||||||
|
shouldComponentUpdate(newProps) {
|
||||||
|
return Object.keys(newProps).some(key => newProps[key] != this.props[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const y = this.props.y
|
||||||
|
const exclude = this.props.exclude
|
||||||
|
let lineToRight
|
||||||
|
let lineToLeft
|
||||||
|
|
||||||
|
if (this.props.leftY) {
|
||||||
|
const middleY = ((this.props.leftY - y) / 2) + y
|
||||||
|
const excludedLine = this.props.leftTemperatureExclude || exclude
|
||||||
|
lineToLeft = makeLine(middleY, y, 'left', excludedLine)
|
||||||
|
}
|
||||||
|
if (this.props.rightY) {
|
||||||
|
const middleY = ((y - this.props.rightY) / 2) + this.props.rightY
|
||||||
|
const excludedLine = this.props.rightTemperatureExclude || exclude
|
||||||
|
lineToRight = makeLine(y, middleY, 'right', excludedLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dotStyle = exclude ? styles.curveDotsExcluded : styles.curveDots
|
||||||
|
const dot = (
|
||||||
|
<View
|
||||||
|
position='absolute'
|
||||||
|
top={y - (dotStyle.height / 2)}
|
||||||
|
left={config.columnMiddle - (dotStyle.width / 2)}
|
||||||
|
style={dotStyle}
|
||||||
|
key='dot'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
return [lineToLeft, lineToRight, dot]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLine(leftY, rightY, direction, excludeLine) {
|
||||||
|
const colWidth = config.columnWidth
|
||||||
|
const heightDiff = -leftY - -rightY
|
||||||
|
const angle = Math.atan2(heightDiff, colWidth / 2)
|
||||||
|
const lineStyle = excludeLine ? styles.curveExcluded : styles.curve
|
||||||
|
// hypotenuse, we add 3px for good measure, because otherwise the lines
|
||||||
|
// don't quite touch at the day border
|
||||||
|
const h = (colWidth / 2) / Math.cos(angle) + 10
|
||||||
|
// the rotation by default rotates from the middle of the line,
|
||||||
|
// but we want the transform origin to be at its beginning
|
||||||
|
// react native doesn't have transformOrigin, so we do this manually
|
||||||
|
// if it's the right line, we put the pivot at 3/4 of the column
|
||||||
|
// if it's to the left, at 1/4
|
||||||
|
const pivot = direction === 'right' ? colWidth / 4 : -(colWidth / 4)
|
||||||
|
const projectedX = -(h - colWidth) / 2 + pivot
|
||||||
|
|
||||||
|
return (<View
|
||||||
|
width={h}
|
||||||
|
position='absolute'
|
||||||
|
top={((leftY + rightY) / 2) - lineStyle.borderWidth / 2}
|
||||||
|
left={projectedX}
|
||||||
|
style={{
|
||||||
|
transform: [
|
||||||
|
{ rotateZ: `${angle}rad` }
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
{...lineStyle}
|
||||||
|
key ={direction}
|
||||||
|
/>)
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<svg version="1.1"
|
||||||
|
baseProfile="full"
|
||||||
|
width="300" height="200"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="
|
||||||
|
M15 3
|
||||||
|
Q16.5 6.8 25 18
|
||||||
|
A12.8 12.8 0 1 1 5 18
|
||||||
|
Q13.5 6.8 15 3z
|
||||||
|
" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 217 B |
@@ -0,0 +1,80 @@
|
|||||||
|
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
||||||
|
import { normalizeToScale } from './y-axis'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const cycle = {
|
||||||
|
status: null
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCurrentCycle(dateString) {
|
||||||
|
cycle.status = getCycleStatusForDay(dateString)
|
||||||
|
if(!cycle.status) {
|
||||||
|
cycle.noMoreCycles = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (cycle.status.phases.preOvulatory) {
|
||||||
|
cycle.startDate = cycle.status.phases.preOvulatory.start.date
|
||||||
|
} else {
|
||||||
|
cycle.startDate = cycle.status.phases.periOvulatory.start.date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateIsInPeriOrPostPhase(dateString) {
|
||||||
|
return (
|
||||||
|
dateString >= cycle.status.phases.periOvulatory.start.date
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function precededByAnotherTempValue(dateString) {
|
||||||
|
return (
|
||||||
|
// we are only interested in days that have a preceding
|
||||||
|
// temp
|
||||||
|
Object.keys(cycle.status.phases).some(phaseName => {
|
||||||
|
return cycle.status.phases[phaseName].cycleDays.some(day => {
|
||||||
|
return day.temperature && day.date < dateString
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// and also a following temp, so we don't draw the line
|
||||||
|
// longer than necessary
|
||||||
|
&&
|
||||||
|
cycle.status.phases.postOvulatory.cycleDays.some(day => {
|
||||||
|
return day.temperature && day.date > dateString
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInTempMeasuringPhase(temperature, dateString) {
|
||||||
|
return (
|
||||||
|
temperature || precededByAnotherTempValue(dateString)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(dateString, temperature) {
|
||||||
|
const ret = {
|
||||||
|
drawLtlAt: null,
|
||||||
|
drawFhmLine: false
|
||||||
|
}
|
||||||
|
if (!cycle.status && !cycle.noMoreCycles) updateCurrentCycle(dateString)
|
||||||
|
if (cycle.noMoreCycles) return ret
|
||||||
|
|
||||||
|
if (dateString < cycle.startDate) updateCurrentCycle(dateString)
|
||||||
|
if (cycle.noMoreCycles) return ret
|
||||||
|
|
||||||
|
const tempShift = cycle.status.temperatureShift
|
||||||
|
|
||||||
|
if (tempShift) {
|
||||||
|
if (tempShift.firstHighMeasurementDay.date === dateString) {
|
||||||
|
ret.drawFhmLine = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
dateIsInPeriOrPostPhase(dateString) &&
|
||||||
|
isInTempMeasuringPhase(temperature, dateString)
|
||||||
|
) {
|
||||||
|
ret.drawLtlAt = normalizeToScale(tempShift.ltl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
-30
@@ -2,44 +2,46 @@ import config from './config'
|
|||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
curve: {
|
curve: {
|
||||||
stroke: '#ffc425',
|
borderStyle: 'solid',
|
||||||
strokeWidth: 2
|
borderColor: '#ffc425',
|
||||||
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
curveExcluded: {
|
curveExcluded: {
|
||||||
stroke: 'lightgrey',
|
borderColor: 'lightgrey',
|
||||||
strokeWidth: 2,
|
borderWidth: 2,
|
||||||
strokeDashArray: [4]
|
borderStyle: 'solid'
|
||||||
},
|
},
|
||||||
curveDots: {
|
curveDots: {
|
||||||
fill: '#00aedb',
|
backgroundColor: '#00aedb',
|
||||||
r: 6
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: 50
|
||||||
},
|
},
|
||||||
curveDotsExcluded: {
|
curveDotsExcluded: {
|
||||||
fill: 'lightgrey',
|
backgroundColor: 'lightgrey',
|
||||||
r: 6
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: 50
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
label: {
|
label: {
|
||||||
date: {
|
date: {
|
||||||
stroke: 'grey',
|
color: 'grey',
|
||||||
fontSize: 10,
|
fontSize: 9,
|
||||||
x: 2,
|
|
||||||
fontWeight: '100'
|
fontWeight: '100'
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
stroke: '#00b159',
|
color: '#00b159',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
x: config.columnMiddle - 1
|
textAlign: 'center'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rect: {
|
rect: {
|
||||||
fill: '#f9f9f9',
|
|
||||||
strokeWidth: 1,
|
|
||||||
stroke: 'grey',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: config.columnWidth,
|
width: config.columnWidth,
|
||||||
height: config.chartHeight
|
height: config.chartHeight,
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderColor: 'grey',
|
||||||
|
borderWidth: 0.5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bleedingIcon: {
|
bleedingIcon: {
|
||||||
@@ -49,9 +51,9 @@ const styles = {
|
|||||||
y: 3
|
y: 3
|
||||||
},
|
},
|
||||||
mucusIcon: {
|
mucusIcon: {
|
||||||
cx: config.columnWidth / 2,
|
width: 12,
|
||||||
cy: 50,
|
height: 12,
|
||||||
r: 10
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
mucusIconShades: [
|
mucusIconShades: [
|
||||||
'#cc99cc',
|
'#cc99cc',
|
||||||
@@ -63,21 +65,29 @@ const styles = {
|
|||||||
yAxis: {
|
yAxis: {
|
||||||
height: config.chartHeight,
|
height: config.chartHeight,
|
||||||
width: config.columnWidth,
|
width: config.columnWidth,
|
||||||
|
borderRightWidth: 0.5,
|
||||||
|
borderColor: 'lightgrey',
|
||||||
|
borderStyle: 'solid'
|
||||||
},
|
},
|
||||||
yAxisLabel: {
|
yAxisLabel: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 3,
|
left: 3,
|
||||||
color: 'grey',
|
color: 'grey',
|
||||||
fontSize: 12,
|
fontSize: 11,
|
||||||
fontWeight: 'bold'
|
textAlign: 'left'
|
||||||
},
|
},
|
||||||
horizontalGrid: {
|
horizontalGrid: {
|
||||||
stroke: 'lightgrey',
|
position:'absolute',
|
||||||
strokeWidth: 1
|
borderColor: 'lightgrey',
|
||||||
|
borderWidth: 0.5,
|
||||||
|
width: '100%',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
left: config.columnWidth
|
||||||
},
|
},
|
||||||
nfpLine: {
|
nfpLine: {
|
||||||
stroke: '#00b159',
|
borderColor: '#00b159',
|
||||||
strokeWidth: 3
|
borderWidth: 2,
|
||||||
|
borderStyle: 'solid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Text, View } from 'react-native'
|
||||||
|
import config from './config'
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
function makeYAxis() {
|
||||||
|
const scale = config.temperatureScale
|
||||||
|
const scaleMin = scale.low
|
||||||
|
const scaleMax = scale.high
|
||||||
|
const numberOfTicks = (scaleMax - scaleMin) * (1 / scale.units)
|
||||||
|
const tickDistance = config.chartHeight / numberOfTicks
|
||||||
|
|
||||||
|
const tickPositions = []
|
||||||
|
const labels = []
|
||||||
|
// for style reasons, we don't want the first and last tick
|
||||||
|
for (let i = 1; i < numberOfTicks - 1; i++) {
|
||||||
|
const y = tickDistance * i
|
||||||
|
const style = styles.yAxisLabel
|
||||||
|
// this eyeballing is sadly necessary because RN does not
|
||||||
|
// support percentage values for transforms, which we'd need
|
||||||
|
// to reliably place the label vertically centered to the grid
|
||||||
|
style.top = y - 8
|
||||||
|
labels.push(
|
||||||
|
<Text
|
||||||
|
style={{...style}}
|
||||||
|
key={i}>
|
||||||
|
{scaleMax - i * scale.units}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
tickPositions.push(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {labels, tickPositions}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const yAxis = makeYAxis()
|
||||||
|
|
||||||
|
export const horizontalGrid = yAxis.tickPositions.map(tick => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
top={tick}
|
||||||
|
{...styles.horizontalGrid}
|
||||||
|
key={tick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export function normalizeToScale(temp) {
|
||||||
|
const scale = config.temperatureScale
|
||||||
|
const valueRelativeToScale = (scale.high - temp) / (scale.high - scale.low)
|
||||||
|
const scaleHeight = config.chartHeight
|
||||||
|
return scaleHeight * valueRelativeToScale
|
||||||
|
}
|
||||||
@@ -98,11 +98,20 @@ export default class DayView extends Component {
|
|||||||
<Text style={styles.symptomDayView}>Desire</Text>
|
<Text style={styles.symptomDayView}>Desire</Text>
|
||||||
<View style={styles.symptomEditButton}>
|
<View style={styles.symptomEditButton}>
|
||||||
<Button
|
<Button
|
||||||
onPress={() => this.showView('desireEditView')}
|
onPress={() => this.showView('DesireEditView')}
|
||||||
title={getLabel('desire', cycleDay.desire)}>
|
title={getLabel('desire', cycleDay.desire)}>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>Sex</Text>
|
||||||
|
<View style={styles.symptomEditButton}>
|
||||||
|
<Button
|
||||||
|
onPress={() => this.showView('SexEditView')}
|
||||||
|
title={getLabel('sex', cycleDay.sex)}>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View >
|
</View >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -132,15 +141,22 @@ function getLabel(symptomName, symptom) {
|
|||||||
typeof mucus.texture === 'number' &&
|
typeof mucus.texture === 'number' &&
|
||||||
typeof mucus.value === 'number'
|
typeof mucus.value === 'number'
|
||||||
) {
|
) {
|
||||||
let mucusLabel = `${feelingLabels[mucus.feeling]} + ${textureLabels[mucus.texture]} ( ${computeSensiplanMucusLabels[mucus.value]} )`
|
let mucusLabel =
|
||||||
|
`${feelingLabels[mucus.feeling]} +
|
||||||
|
${textureLabels[mucus.texture]}
|
||||||
|
( ${computeSensiplanMucusLabels[mucus.value]} )`
|
||||||
if (mucus.exclude) mucusLabel = "( " + mucusLabel + " )"
|
if (mucus.exclude) mucusLabel = "( " + mucusLabel + " )"
|
||||||
return mucusLabel
|
return mucusLabel
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cervix: cervix => {
|
cervix: cervix => {
|
||||||
if (cervix.opening > -1 && cervix.firmness > -1) {
|
if (cervix.opening > -1 && cervix.firmness > -1) {
|
||||||
let cervixLabel = `${openingLabels[cervix.opening]} + ${firmnessLabels[cervix.firmness]}`
|
let cervixLabel =
|
||||||
if (cervix.position > -1) cervixLabel += `+ ${positionLabels[cervix.position]}`
|
`${openingLabels[cervix.opening]} +
|
||||||
|
${firmnessLabels[cervix.firmness]}`
|
||||||
|
if (cervix.position > -1) {
|
||||||
|
cervixLabel += `+ ${positionLabels[cervix.position]}`
|
||||||
|
}
|
||||||
if (cervix.exclude) cervixLabel = "( " + cervixLabel + " )"
|
if (cervix.exclude) cervixLabel = "( " + cervixLabel + " )"
|
||||||
return cervixLabel
|
return cervixLabel
|
||||||
}
|
}
|
||||||
@@ -153,6 +169,17 @@ function getLabel(symptomName, symptom) {
|
|||||||
const desireLabel = `${intensityLabels[desire.value]}`
|
const desireLabel = `${intensityLabels[desire.value]}`
|
||||||
return desireLabel
|
return desireLabel
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
sex: sex => {
|
||||||
|
let sexLabel = ''
|
||||||
|
if ( sex.solo || sex.partner ) {
|
||||||
|
sexLabel += 'Activity '
|
||||||
|
}
|
||||||
|
if (sex.condom || sex.pill || sex.iud ||
|
||||||
|
sex.patch || sex.ring || sex.implant || sex.other) {
|
||||||
|
sexLabel += 'Contraceptive'
|
||||||
|
}
|
||||||
|
return sexLabel ? sexLabel : 'edit'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,19 @@ export const cervixOpening = ['closed', 'medium', 'open']
|
|||||||
export const cervixFirmness = ['hard', 'soft']
|
export const cervixFirmness = ['hard', 'soft']
|
||||||
export const cervixPosition = ['low', 'medium', 'high']
|
export const cervixPosition = ['low', 'medium', 'high']
|
||||||
export const intensity = ['low', 'medium', 'high']
|
export const intensity = ['low', 'medium', 'high']
|
||||||
|
export const sexActivity = {
|
||||||
|
solo: 'Solo',
|
||||||
|
partner: 'Partner'
|
||||||
|
}
|
||||||
|
export const contraceptives = {
|
||||||
|
condom: 'Condom',
|
||||||
|
pill: 'Pill',
|
||||||
|
iud: 'IUD',
|
||||||
|
patch: 'Patch',
|
||||||
|
ring: 'Ring',
|
||||||
|
implant: 'Implant',
|
||||||
|
other: 'Other'
|
||||||
|
}
|
||||||
|
|
||||||
export const fertilityStatus = {
|
export const fertilityStatus = {
|
||||||
fertile: 'fertile',
|
fertile: 'fertile',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import MucusEditView from './mucus'
|
|||||||
import CervixEditView from './cervix'
|
import CervixEditView from './cervix'
|
||||||
import NoteEditView from './note'
|
import NoteEditView from './note'
|
||||||
import DesireEditView from './desire'
|
import DesireEditView from './desire'
|
||||||
|
import SexEditView from './sex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
BleedingEditView,
|
BleedingEditView,
|
||||||
@@ -11,5 +12,6 @@ export default {
|
|||||||
MucusEditView,
|
MucusEditView,
|
||||||
CervixEditView,
|
CervixEditView,
|
||||||
NoteEditView,
|
NoteEditView,
|
||||||
DesireEditView
|
DesireEditView,
|
||||||
}
|
SexEditView
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
CheckBox,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
|
import styles from '../../../styles'
|
||||||
|
import { saveSymptom } from '../../../db'
|
||||||
|
import {
|
||||||
|
sexActivity as activityLabels,
|
||||||
|
contraceptives as contraceptiveLabels
|
||||||
|
} from '../labels/labels'
|
||||||
|
|
||||||
|
export default class Sex extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.cycleDay = props.cycleDay
|
||||||
|
this.state = {}
|
||||||
|
if (this.cycleDay.sex !== null ) {
|
||||||
|
Object.assign(this.state, this.cycleDay.sex)
|
||||||
|
// We make sure other is always true when there is a note,
|
||||||
|
// e.g. when import is messed up.
|
||||||
|
if (this.cycleDay.sex && this.cycleDay.sex.note) {
|
||||||
|
this.state.other = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.symptomEditView}>
|
||||||
|
<Text style={styles.symptomDayView}>SEX</Text>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>{activityLabels.solo}</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.solo}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({solo: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={styles.symptomDayView}>{activityLabels.partner}</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.partner}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({partner: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.symptomDayView}>CONTRACEPTIVES</Text>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.condom}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.condom}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({condom: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.pill}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.pill}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({pill: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.iud}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.iud}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({iud: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.patch}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.patch}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({patch: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.ring}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.ring}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({ring: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.implant}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.implant}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({implant: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.symptomViewRowInline}>
|
||||||
|
<Text style={styles.symptomDayView}>
|
||||||
|
{contraceptiveLabels.other}
|
||||||
|
</Text>
|
||||||
|
<CheckBox
|
||||||
|
value={this.state.other}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
this.setState({
|
||||||
|
other: val,
|
||||||
|
focusTextArea: true
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{ this.state.other &&
|
||||||
|
<TextInput
|
||||||
|
autoFocus={this.state.focusTextArea}
|
||||||
|
multiline={true}
|
||||||
|
placeholder="Enter"
|
||||||
|
value={this.state.note}
|
||||||
|
onChangeText={(val) => {
|
||||||
|
this.setState({note: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<View style={styles.actionButtonRow}>
|
||||||
|
{this.props.makeActionButtons(
|
||||||
|
{
|
||||||
|
symptom: 'sex',
|
||||||
|
cycleDay: this.cycleDay,
|
||||||
|
saveAction: () => {
|
||||||
|
const copyOfState = Object.assign({}, this.state)
|
||||||
|
if (!copyOfState.other) {
|
||||||
|
copyOfState.note = null
|
||||||
|
}
|
||||||
|
saveSymptom('sex', this.cycleDay, copyOfState)
|
||||||
|
},
|
||||||
|
saveDisabled: Object.values(this.state).every(value => !value)
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,8 +70,12 @@ export default class Temp extends Component {
|
|||||||
mode="time"
|
mode="time"
|
||||||
isVisible={this.state.isTimePickerVisible}
|
isVisible={this.state.isTimePickerVisible}
|
||||||
onConfirm={jsDate => {
|
onConfirm={jsDate => {
|
||||||
|
let hours = jsDate.getHours()
|
||||||
|
if (hours < 10) hours = `0${hours}`
|
||||||
|
let minutes = jsDate.getMinutes()
|
||||||
|
if (minutes < 10) minutes = `0${minutes}`
|
||||||
this.setState({
|
this.setState({
|
||||||
time: `${jsDate.getHours()}:${jsDate.getMinutes()}`,
|
time: `${hours}:${minutes}`,
|
||||||
isTimePickerVisible: false
|
isTimePickerVisible: false
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|||||||
+1
-26
@@ -42,11 +42,10 @@ export default class Home extends Component {
|
|||||||
const todayDateString = LocalDate.now().toString()
|
const todayDateString = LocalDate.now().toString()
|
||||||
const cycleDay = getOrCreateCycleDay(todayDateString)
|
const cycleDay = getOrCreateCycleDay(todayDateString)
|
||||||
const navigate = this.props.navigation.navigate
|
const navigate = this.props.navigation.navigate
|
||||||
navigate('cycleDay', { cycleDay })
|
navigate('CycleDay', { cycleDay })
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const navigate = this.props.navigation.navigate
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<Text style={styles.welcome}>{this.state.welcomeText}</Text>
|
<Text style={styles.welcome}>{this.state.welcomeText}</Text>
|
||||||
@@ -57,24 +56,6 @@ export default class Home extends Component {
|
|||||||
title="Edit symptoms for today">
|
title="Edit symptoms for today">
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.homeButton}>
|
|
||||||
<Button
|
|
||||||
onPress={() => navigate('calendar')}
|
|
||||||
title="Go to calendar">
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
<View style={styles.homeButton}>
|
|
||||||
<Button
|
|
||||||
onPress={() => navigate('chart')}
|
|
||||||
title="Go to chart">
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
<View style={styles.homeButton}>
|
|
||||||
<Button
|
|
||||||
onPress={() => navigate('settings')}
|
|
||||||
title="Go to settings">
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
<View style={styles.homeButton}>
|
<View style={styles.homeButton}>
|
||||||
<Button
|
<Button
|
||||||
onPress={() => fillWithDummyData()}
|
onPress={() => fillWithDummyData()}
|
||||||
@@ -87,12 +68,6 @@ export default class Home extends Component {
|
|||||||
title="delete everything">
|
title="delete everything">
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.homeButton}>
|
|
||||||
<Button
|
|
||||||
onPress={() => navigate('stats')}
|
|
||||||
title="Go to stats">
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
|
|||||||
+30
-7
@@ -1,12 +1,35 @@
|
|||||||
export const settings = {
|
export const settings = {
|
||||||
errors: {
|
shared: {
|
||||||
noData: 'There is no data to export',
|
cancel: 'Cancel',
|
||||||
couldNotConvert: 'Could not convert data to CSV',
|
errorTitle: 'Error',
|
||||||
problemSharing: 'There was a problem sharing the data export file'
|
successTitle: 'Success'
|
||||||
},
|
},
|
||||||
exportTitle: 'My Drip data export',
|
export: {
|
||||||
exportSubject: 'My Drip data export',
|
errors: {
|
||||||
buttonLabel: 'Export data'
|
noData: 'There is no data to export',
|
||||||
|
couldNotConvert: 'Could not convert data to CSV',
|
||||||
|
problemSharing: 'There was a problem sharing the data export file'
|
||||||
|
},
|
||||||
|
title: 'My Drip data export',
|
||||||
|
subject: 'My Drip data export',
|
||||||
|
button: 'Export data',
|
||||||
|
},
|
||||||
|
import: {
|
||||||
|
button: 'Import data',
|
||||||
|
title: 'Keep existing data?',
|
||||||
|
message: `There are two options for the import:
|
||||||
|
1. Keep existing cycle days and replace only the ones in the import file.
|
||||||
|
2. Delete all existing cycle days and import cycle days from file.`,
|
||||||
|
replaceOption: 'Import and replace',
|
||||||
|
deleteOption: 'Import and delete existing',
|
||||||
|
errors: {
|
||||||
|
couldNotOpenFile: 'Could not open file',
|
||||||
|
postFix: 'No data was imported or changed'
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
message: 'Data successfully imported'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stats = {
|
export const stats = {
|
||||||
|
|||||||
+94
-27
@@ -7,9 +7,12 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
|
||||||
import Share from 'react-native-share'
|
import Share from 'react-native-share'
|
||||||
import getDataAsCsvDataUri from '../lib/export-to-csv'
|
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
|
||||||
|
import rnfs from 'react-native-fs'
|
||||||
import styles from '../styles/index'
|
import styles from '../styles/index'
|
||||||
import { settings as labels } from './labels'
|
import { settings as labels } from './labels'
|
||||||
|
import getDataAsCsvDataUri from '../lib/import-export/export-to-csv'
|
||||||
|
import importCsv from '../lib/import-export/import-from-csv'
|
||||||
|
|
||||||
export default class Settings extends Component {
|
export default class Settings extends Component {
|
||||||
render() {
|
render() {
|
||||||
@@ -18,36 +21,100 @@ export default class Settings extends Component {
|
|||||||
<View style={styles.homeButtons}>
|
<View style={styles.homeButtons}>
|
||||||
<View style={styles.homeButton}>
|
<View style={styles.homeButton}>
|
||||||
<Button
|
<Button
|
||||||
onPress={async () => {
|
onPress={ openShareDialogAndExport }
|
||||||
let data
|
title={labels.export.button}>
|
||||||
try {
|
</Button>
|
||||||
data = getDataAsCsvDataUri()
|
</View>
|
||||||
if (!data) {
|
<View style={styles.homeButton}>
|
||||||
return Alert.alert(labels.errors.noData)
|
<Button
|
||||||
}
|
title={labels.import.button}
|
||||||
} catch (err) {
|
onPress={ openImportDialogAndImport }>
|
||||||
console.error(err)
|
|
||||||
return Alert.alert(labels.errors.couldNotConvert)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Share.open({
|
|
||||||
title: labels.exportTitle,
|
|
||||||
url: data,
|
|
||||||
subject: labels.exportSubject,
|
|
||||||
type: 'text/csv',
|
|
||||||
showAppsToView: true
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
return Alert.alert(labels.errors.problemSharing)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title={labels.buttonLabel}>
|
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openShareDialogAndExport() {
|
||||||
|
let data
|
||||||
|
try {
|
||||||
|
data = getDataAsCsvDataUri()
|
||||||
|
if (!data) {
|
||||||
|
return alertError(labels.errors.noData)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return alertError(labels.errors.couldNotConvert)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Share.open({
|
||||||
|
title: labels.export.title,
|
||||||
|
url: data,
|
||||||
|
subject: labels.export.subject,
|
||||||
|
type: 'text/csv',
|
||||||
|
showAppsToView: true
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return alertError(labels.export.errors.problemSharing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openImportDialogAndImport() {
|
||||||
|
Alert.alert(
|
||||||
|
labels.import.title,
|
||||||
|
labels.import.message,
|
||||||
|
[{
|
||||||
|
text: labels.import.replaceOption,
|
||||||
|
onPress: () => getFileContentAndImport({ deleteExisting: false })
|
||||||
|
}, {
|
||||||
|
text: labels.import.deleteOption,
|
||||||
|
onPress: () => getFileContentAndImport({ deleteExisting: true })
|
||||||
|
}, {
|
||||||
|
text: labels.shared.cancel, style: 'cancel', onPress: () => { }
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFileContentAndImport({ deleteExisting }) {
|
||||||
|
let fileInfo
|
||||||
|
try {
|
||||||
|
fileInfo = await new Promise((resolve, reject) => {
|
||||||
|
DocumentPicker.show({
|
||||||
|
filetype: [DocumentPickerUtil.allFiles()],
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) return reject(err)
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
// because cancelling also triggers an error, we do nothing here
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileContent
|
||||||
|
try {
|
||||||
|
fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
|
||||||
|
} catch (err) {
|
||||||
|
return importError(labels.import.errors.couldNotOpenFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await importCsv(fileContent, deleteExisting)
|
||||||
|
Alert.alert(labels.import.success.title, labels.import.success.message)
|
||||||
|
} catch(err) {
|
||||||
|
importError(err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function alertError(msg) {
|
||||||
|
Alert.alert(labels.shared.errorTitle, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
function importError(msg) {
|
||||||
|
const postFixed = `${msg}\n\n${labels.import.errors.postFix}`
|
||||||
|
alertError(postFixed)
|
||||||
}
|
}
|
||||||
+63
-17
@@ -1,5 +1,5 @@
|
|||||||
import Realm from 'realm'
|
import Realm from 'realm'
|
||||||
import { LocalDate } from 'js-joda'
|
import { LocalDate, ChronoUnit } from 'js-joda'
|
||||||
import {
|
import {
|
||||||
cycleWithTempAndNoMucusShift,
|
cycleWithTempAndNoMucusShift,
|
||||||
cycleWithFhm,
|
cycleWithFhm,
|
||||||
@@ -60,6 +60,22 @@ const DesireSchema = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SexSchema = {
|
||||||
|
name: 'Sex',
|
||||||
|
properties: {
|
||||||
|
solo: { type: 'bool', optional: true },
|
||||||
|
partner: { type: 'bool', optional: true },
|
||||||
|
condom: { type: 'bool', optional: true },
|
||||||
|
pill: { type: 'bool', optional: true },
|
||||||
|
iud: { type: 'bool', optional: true },
|
||||||
|
patch: { type: 'bool', optional: true },
|
||||||
|
ring: { type: 'bool', optional: true },
|
||||||
|
implant: { type: 'bool', optional: true },
|
||||||
|
other: { type: 'bool', optional: true },
|
||||||
|
note: { type: 'string', optional: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CycleDaySchema = {
|
const CycleDaySchema = {
|
||||||
name: 'CycleDay',
|
name: 'CycleDay',
|
||||||
primaryKey: 'date',
|
primaryKey: 'date',
|
||||||
@@ -88,6 +104,10 @@ const CycleDaySchema = {
|
|||||||
desire: {
|
desire: {
|
||||||
type: 'Desire',
|
type: 'Desire',
|
||||||
optional: true
|
optional: true
|
||||||
|
},
|
||||||
|
sex: {
|
||||||
|
type: 'Sex',
|
||||||
|
optional: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +120,8 @@ const realmConfig = {
|
|||||||
MucusSchema,
|
MucusSchema,
|
||||||
CervixSchema,
|
CervixSchema,
|
||||||
NoteSchema,
|
NoteSchema,
|
||||||
DesireSchema
|
DesireSchema,
|
||||||
|
SexSchema
|
||||||
],
|
],
|
||||||
// we only want this in dev mode
|
// we only want this in dev mode
|
||||||
deleteRealmIfMigrationNeeded: true
|
deleteRealmIfMigrationNeeded: true
|
||||||
@@ -175,24 +196,46 @@ function getPreviousTemperature(cycleDay) {
|
|||||||
return winner.temperature.value
|
return winner.temperature.value
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColumnNamesForCsv() {
|
const schema = db.schema.reduce((acc, curr) => {
|
||||||
return getPrefixedKeys('CycleDay')
|
acc[curr.name] = curr.properties
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
function getPrefixedKeys(schemaName, prefix) {
|
function tryToCreateCycleDay(day, i) {
|
||||||
const schema = db.schema.find(x => x.name === schemaName).properties
|
try {
|
||||||
return Object.keys(schema).reduce((acc, key) => {
|
db.create('CycleDay', day)
|
||||||
const prefixedKey = prefix ? [prefix, key].join('.') : key
|
} catch (err) {
|
||||||
const childSchemaName = schema[key].objectType
|
const msg = `Line ${i + 1}(${day.date}): ${err.message}`
|
||||||
if (!childSchemaName) {
|
throw new Error(msg)
|
||||||
acc.push(prefixedKey)
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
acc.push(...getPrefixedKeys(childSchemaName, prefixedKey))
|
|
||||||
return acc
|
|
||||||
}, [])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAmountOfCycleDays() {
|
||||||
|
const amountOfCycleDays = cycleDaysSortedByDate.length
|
||||||
|
if (!amountOfCycleDays) return 0
|
||||||
|
const earliest = cycleDaysSortedByDate[amountOfCycleDays - 1]
|
||||||
|
const today = LocalDate.now()
|
||||||
|
const earliestAsLocalDate = LocalDate.parse(earliest.date)
|
||||||
|
return earliestAsLocalDate.until(today, ChronoUnit.DAYS)
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryToImportWithDelete(cycleDays) {
|
||||||
|
db.write(() => {
|
||||||
|
db.delete(db.objects('CycleDay'))
|
||||||
|
cycleDays.forEach(tryToCreateCycleDay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryToImportWithoutDelete(cycleDays) {
|
||||||
|
db.write(() => {
|
||||||
|
cycleDays.forEach((day, i) => {
|
||||||
|
const existing = getCycleDay(day.date)
|
||||||
|
if (existing) db.delete(existing)
|
||||||
|
tryToCreateCycleDay(day, i)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
saveSymptom,
|
saveSymptom,
|
||||||
getOrCreateCycleDay,
|
getOrCreateCycleDay,
|
||||||
@@ -203,5 +246,8 @@ export {
|
|||||||
deleteAll,
|
deleteAll,
|
||||||
getPreviousTemperature,
|
getPreviousTemperature,
|
||||||
getCycleDay,
|
getCycleDay,
|
||||||
getColumnNamesForCsv
|
getAmountOfCycleDays,
|
||||||
|
schema,
|
||||||
|
tryToImportWithDelete,
|
||||||
|
tryToImportWithoutDelete
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,27 @@
|
|||||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
|
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
|
||||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||||
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
|
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
|
||||||
EFD2F9E5E59248D9AD6EBABA /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F4A1722D79F04D5EB528E21A /* libRNSVG.a */; };
|
|
||||||
934282049FA3497D9062CEC1 /* libRNSVG-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5ADC07BB282A481EBD5FD2A2 /* libRNSVG-tvOS.a */; };
|
|
||||||
089A8A31B3244EB381D3BA67 /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A5827160B914D2B99C47381 /* libRealmReact.a */; };
|
089A8A31B3244EB381D3BA67 /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A5827160B914D2B99C47381 /* libRealmReact.a */; };
|
||||||
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AEBF0735214455AAEDF56D5 /* libc++.tbd */; };
|
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AEBF0735214455AAEDF56D5 /* libc++.tbd */; };
|
||||||
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C8B91E0A747B3883A0D56 /* libz.tbd */; };
|
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C8B91E0A747B3883A0D56 /* libz.tbd */; };
|
||||||
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */; };
|
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */; };
|
||||||
|
AED64B7892744F21B3A156BB /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5039D0A572B4BBCB7995891 /* libRNVectorIcons.a */; };
|
||||||
|
283136C9CE964E07BD52BE20 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A2811C9225AC4EAC93FCD2DB /* Entypo.ttf */; };
|
||||||
|
87508EF4BE7548878981BE9E /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B41CDA1146DB4492B1796444 /* EvilIcons.ttf */; };
|
||||||
|
4E04B0A1FECA4915AD4A6CCF /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F59A471BDE4144A1A41D4B52 /* Feather.ttf */; };
|
||||||
|
20146F2E2E9E4EB289472E81 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EE8CBB8533DA48A9A78697C3 /* FontAwesome.ttf */; };
|
||||||
|
AF2EAEC3772C41A49A4AF117 /* FontAwesome5_Brands.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 044ECCD7D9B8470782A453CA /* FontAwesome5_Brands.ttf */; };
|
||||||
|
3F0F9C6368674C66AEAC3807 /* FontAwesome5_Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C783C08EEEB541D2A30FEE44 /* FontAwesome5_Regular.ttf */; };
|
||||||
|
7D48A7B9435741CCA9678C42 /* FontAwesome5_Solid.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0BE71E2D5A634744A662DF44 /* FontAwesome5_Solid.ttf */; };
|
||||||
|
8E4308D3D8614B0BACABF058 /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EA87C1DB160640E0907EE056 /* Foundation.ttf */; };
|
||||||
|
DB93C03E56074FB78F7F5B7C /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8F2C7294680449AEA698AF76 /* Ionicons.ttf */; };
|
||||||
|
96687426D3D64D0F8E15FE4B /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5D8CEEF11CF346C7A641F4EC /* MaterialCommunityIcons.ttf */; };
|
||||||
|
DF31809C83AD48569741458C /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B39DD260535A4B0EB31A3E0A /* MaterialIcons.ttf */; };
|
||||||
|
DB91E6CCC3EB4A549D947797 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4902D5DCD46748BD8DC403FD /* Octicons.ttf */; };
|
||||||
|
3DF2498A20844F298CD84CC3 /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E954835D62BD45F0A5FFC523 /* SimpleLineIcons.ttf */; };
|
||||||
|
A1410AC4C98A49B2820D9E45 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B6F5078F7DEC470782757471 /* Zocial.ttf */; };
|
||||||
|
29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */; };
|
||||||
|
17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84CCEBD3B2C44758853BC941 /* libRNFS.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -348,15 +363,32 @@
|
|||||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
|
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
|
||||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
|
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
|
||||||
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
|
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
|
||||||
8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */ = {isa = PBXFileReference; name = "RNSVG.xcodeproj"; path = "../node_modules/react-native-svg/ios/RNSVG.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
|
||||||
F4A1722D79F04D5EB528E21A /* libRNSVG.a */ = {isa = PBXFileReference; name = "libRNSVG.a"; path = "libRNSVG.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
|
||||||
5ADC07BB282A481EBD5FD2A2 /* libRNSVG-tvOS.a */ = {isa = PBXFileReference; name = "libRNSVG-tvOS.a"; path = "libRNSVG-tvOS.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
|
||||||
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; name = "RealmReact.xcodeproj"; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; name = "RealmReact.xcodeproj"; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
7A5827160B914D2B99C47381 /* libRealmReact.a */ = {isa = PBXFileReference; name = "libRealmReact.a"; path = "libRealmReact.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
7A5827160B914D2B99C47381 /* libRealmReact.a */ = {isa = PBXFileReference; name = "libRealmReact.a"; path = "libRealmReact.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
9AEBF0735214455AAEDF56D5 /* libc++.tbd */ = {isa = PBXFileReference; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
|
9AEBF0735214455AAEDF56D5 /* libc++.tbd */ = {isa = PBXFileReference; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
CD8C8B91E0A747B3883A0D56 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
|
CD8C8B91E0A747B3883A0D56 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */ = {isa = PBXFileReference; name = "RNShare.xcodeproj"; path = "../node_modules/react-native-share/ios/RNShare.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */ = {isa = PBXFileReference; name = "RNShare.xcodeproj"; path = "../node_modules/react-native-share/ios/RNShare.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */ = {isa = PBXFileReference; name = "libRNShare.a"; path = "libRNShare.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */ = {isa = PBXFileReference; name = "libRNShare.a"; path = "libRNShare.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
D1E5ACC4B66345868F556374 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; name = "RNVectorIcons.xcodeproj"; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
F5039D0A572B4BBCB7995891 /* libRNVectorIcons.a */ = {isa = PBXFileReference; name = "libRNVectorIcons.a"; path = "libRNVectorIcons.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
A2811C9225AC4EAC93FCD2DB /* Entypo.ttf */ = {isa = PBXFileReference; name = "Entypo.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
B41CDA1146DB4492B1796444 /* EvilIcons.ttf */ = {isa = PBXFileReference; name = "EvilIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
F59A471BDE4144A1A41D4B52 /* Feather.ttf */ = {isa = PBXFileReference; name = "Feather.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
EE8CBB8533DA48A9A78697C3 /* FontAwesome.ttf */ = {isa = PBXFileReference; name = "FontAwesome.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
044ECCD7D9B8470782A453CA /* FontAwesome5_Brands.ttf */ = {isa = PBXFileReference; name = "FontAwesome5_Brands.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
C783C08EEEB541D2A30FEE44 /* FontAwesome5_Regular.ttf */ = {isa = PBXFileReference; name = "FontAwesome5_Regular.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
0BE71E2D5A634744A662DF44 /* FontAwesome5_Solid.ttf */ = {isa = PBXFileReference; name = "FontAwesome5_Solid.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
EA87C1DB160640E0907EE056 /* Foundation.ttf */ = {isa = PBXFileReference; name = "Foundation.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
8F2C7294680449AEA698AF76 /* Ionicons.ttf */ = {isa = PBXFileReference; name = "Ionicons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
5D8CEEF11CF346C7A641F4EC /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; name = "MaterialCommunityIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
B39DD260535A4B0EB31A3E0A /* MaterialIcons.ttf */ = {isa = PBXFileReference; name = "MaterialIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
4902D5DCD46748BD8DC403FD /* Octicons.ttf */ = {isa = PBXFileReference; name = "Octicons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
E954835D62BD45F0A5FFC523 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; name = "SimpleLineIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
B6F5078F7DEC470782757471 /* Zocial.ttf */ = {isa = PBXFileReference; name = "Zocial.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */ = {isa = PBXFileReference; name = "RNDocumentPicker.xcodeproj"; path = "../node_modules/react-native-document-picker/ios/RNDocumentPicker.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */ = {isa = PBXFileReference; name = "libRNDocumentPicker.a"; path = "libRNDocumentPicker.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */ = {isa = PBXFileReference; name = "RNFS.xcodeproj"; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
84CCEBD3B2C44758853BC941 /* libRNFS.a */ = {isa = PBXFileReference; name = "libRNFS.a"; path = "libRNFS.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -385,11 +417,13 @@
|
|||||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
||||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
||||||
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
|
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
|
||||||
EFD2F9E5E59248D9AD6EBABA /* libRNSVG.a in Frameworks */,
|
|
||||||
089A8A31B3244EB381D3BA67 /* libRealmReact.a in Frameworks */,
|
089A8A31B3244EB381D3BA67 /* libRealmReact.a in Frameworks */,
|
||||||
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */,
|
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */,
|
||||||
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */,
|
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */,
|
||||||
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */,
|
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */,
|
||||||
|
AED64B7892744F21B3A156BB /* libRNVectorIcons.a in Frameworks */,
|
||||||
|
29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */,
|
||||||
|
17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -579,9 +613,11 @@
|
|||||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
|
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
|
||||||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
|
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
|
||||||
8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */,
|
|
||||||
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */,
|
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */,
|
||||||
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */,
|
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */,
|
||||||
|
D1E5ACC4B66345868F556374 /* RNVectorIcons.xcodeproj */,
|
||||||
|
1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */,
|
||||||
|
49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */,
|
||||||
);
|
);
|
||||||
name = Libraries;
|
name = Libraries;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -603,6 +639,7 @@
|
|||||||
00E356EF1AD99517003FC87E /* dripTests */,
|
00E356EF1AD99517003FC87E /* dripTests */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
|
006C39A0B9774387BC5ACA43 /* Resources */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -629,6 +666,28 @@
|
|||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
006C39A0B9774387BC5ACA43 /* Resources */ = {
|
||||||
|
isa = "PBXGroup";
|
||||||
|
children = (
|
||||||
|
A2811C9225AC4EAC93FCD2DB /* Entypo.ttf */,
|
||||||
|
B41CDA1146DB4492B1796444 /* EvilIcons.ttf */,
|
||||||
|
F59A471BDE4144A1A41D4B52 /* Feather.ttf */,
|
||||||
|
EE8CBB8533DA48A9A78697C3 /* FontAwesome.ttf */,
|
||||||
|
044ECCD7D9B8470782A453CA /* FontAwesome5_Brands.ttf */,
|
||||||
|
C783C08EEEB541D2A30FEE44 /* FontAwesome5_Regular.ttf */,
|
||||||
|
0BE71E2D5A634744A662DF44 /* FontAwesome5_Solid.ttf */,
|
||||||
|
EA87C1DB160640E0907EE056 /* Foundation.ttf */,
|
||||||
|
8F2C7294680449AEA698AF76 /* Ionicons.ttf */,
|
||||||
|
5D8CEEF11CF346C7A641F4EC /* MaterialCommunityIcons.ttf */,
|
||||||
|
B39DD260535A4B0EB31A3E0A /* MaterialIcons.ttf */,
|
||||||
|
4902D5DCD46748BD8DC403FD /* Octicons.ttf */,
|
||||||
|
E954835D62BD45F0A5FFC523 /* SimpleLineIcons.ttf */,
|
||||||
|
B6F5078F7DEC470782757471 /* Zocial.ttf */,
|
||||||
|
);
|
||||||
|
name = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
path = "";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -1075,6 +1134,20 @@
|
|||||||
files = (
|
files = (
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
|
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
|
||||||
|
283136C9CE964E07BD52BE20 /* Entypo.ttf in Resources */,
|
||||||
|
87508EF4BE7548878981BE9E /* EvilIcons.ttf in Resources */,
|
||||||
|
4E04B0A1FECA4915AD4A6CCF /* Feather.ttf in Resources */,
|
||||||
|
20146F2E2E9E4EB289472E81 /* FontAwesome.ttf in Resources */,
|
||||||
|
AF2EAEC3772C41A49A4AF117 /* FontAwesome5_Brands.ttf in Resources */,
|
||||||
|
3F0F9C6368674C66AEAC3807 /* FontAwesome5_Regular.ttf in Resources */,
|
||||||
|
7D48A7B9435741CCA9678C42 /* FontAwesome5_Solid.ttf in Resources */,
|
||||||
|
8E4308D3D8614B0BACABF058 /* Foundation.ttf in Resources */,
|
||||||
|
DB93C03E56074FB78F7F5B7C /* Ionicons.ttf in Resources */,
|
||||||
|
96687426D3D64D0F8E15FE4B /* MaterialCommunityIcons.ttf in Resources */,
|
||||||
|
DF31809C83AD48569741458C /* MaterialIcons.ttf in Resources */,
|
||||||
|
DB91E6CCC3EB4A549D947797 /* Octicons.ttf in Resources */,
|
||||||
|
3DF2498A20844F298CD84CC3 /* SimpleLineIcons.ttf in Resources */,
|
||||||
|
A1410AC4C98A49B2820D9E45 /* Zocial.ttf in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1209,12 +1282,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -1236,12 +1313,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -1263,9 +1344,11 @@
|
|||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -1286,9 +1369,11 @@
|
|||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -1319,12 +1404,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -1355,12 +1444,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -1390,12 +1483,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -1425,12 +1522,16 @@
|
|||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
|
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||||
);
|
);
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
|
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
"$(SRCROOT)/../node_modules/react-native-share/ios",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|||||||
+18
-2
@@ -39,9 +39,8 @@
|
|||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExceptionDomains</key>
|
<key>NSExceptionDomains</key>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -52,5 +51,22 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Entypo.ttf</string>
|
||||||
|
<string>EvilIcons.ttf</string>
|
||||||
|
<string>Feather.ttf</string>
|
||||||
|
<string>FontAwesome.ttf</string>
|
||||||
|
<string>FontAwesome5_Brands.ttf</string>
|
||||||
|
<string>FontAwesome5_Regular.ttf</string>
|
||||||
|
<string>FontAwesome5_Solid.ttf</string>
|
||||||
|
<string>Foundation.ttf</string>
|
||||||
|
<string>Ionicons.ttf</string>
|
||||||
|
<string>MaterialCommunityIcons.ttf</string>
|
||||||
|
<string>MaterialIcons.ttf</string>
|
||||||
|
<string>Octicons.ttf</string>
|
||||||
|
<string>SimpleLineIcons.ttf</string>
|
||||||
|
<string>Zocial.ttf</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import objectPath from 'object-path'
|
import objectPath from 'object-path'
|
||||||
import { Base64 } from 'js-base64'
|
import { Base64 } from 'js-base64'
|
||||||
|
import { cycleDaysSortedByDate } from '../../db'
|
||||||
import { getColumnNamesForCsv, cycleDaysSortedByDate } from '../db'
|
import getColumnNamesForCsv from './get-csv-column-names'
|
||||||
|
|
||||||
export default function makeDataURI() {
|
export default function makeDataURI() {
|
||||||
if (!cycleDaysSortedByDate.length) return null
|
if (!cycleDaysSortedByDate.length) return null
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { schema } from '../../db'
|
||||||
|
|
||||||
|
export default function getColumnNamesForCsv() {
|
||||||
|
return getPrefixedKeys('CycleDay')
|
||||||
|
|
||||||
|
function getPrefixedKeys(schemaName, prefix) {
|
||||||
|
const model = schema[schemaName]
|
||||||
|
return Object.keys(model).reduce((acc, key) => {
|
||||||
|
const prefixedKey = prefix ? [prefix, key].join('.') : key
|
||||||
|
const childSchemaName = model[key].objectType
|
||||||
|
if (!childSchemaName) {
|
||||||
|
acc.push(prefixedKey)
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
acc.push(...getPrefixedKeys(childSchemaName, prefixedKey))
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import csvParser from 'csvtojson'
|
||||||
|
import isObject from 'isobject'
|
||||||
|
import { schema, tryToImportWithDelete, tryToImportWithoutDelete } from '../../db'
|
||||||
|
import getColumnNamesForCsv from './get-csv-column-names'
|
||||||
|
|
||||||
|
export default async function importCsv(csv, deleteFirst) {
|
||||||
|
const parseFuncs = {
|
||||||
|
bool: val => {
|
||||||
|
if (val.toLowerCase() === 'true') return true
|
||||||
|
if (val.toLowerCase() === 'false') return false
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
int: parseNumberIfPossible,
|
||||||
|
float: parseNumberIfPossible,
|
||||||
|
double: parseNumberIfPossible,
|
||||||
|
string: val => val
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNumberIfPossible(val) {
|
||||||
|
// Number and parseFloat catch different cases of weirdness,
|
||||||
|
// so we test them both
|
||||||
|
if (isNaN(Number(val)) || isNaN(parseFloat(val))) return val
|
||||||
|
return Number(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
ignoreEmpty: true,
|
||||||
|
colParser: getColumnNamesForCsv().reduce((acc, colName) => {
|
||||||
|
const path = colName.split('.')
|
||||||
|
const dbType = getDbType(schema.CycleDay, path)
|
||||||
|
acc[colName] = item => {
|
||||||
|
if (item === '') return null
|
||||||
|
return parseFuncs[dbType](item)
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cycleDays = await csvParser(config)
|
||||||
|
.fromString(csv)
|
||||||
|
.on('header', validateHeaders)
|
||||||
|
|
||||||
|
//remove symptoms where all fields are null
|
||||||
|
putNullForEmptySymptoms(cycleDays)
|
||||||
|
|
||||||
|
if (deleteFirst) {
|
||||||
|
tryToImportWithDelete(cycleDays)
|
||||||
|
} else {
|
||||||
|
tryToImportWithoutDelete(cycleDays)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateHeaders(headers) {
|
||||||
|
const expectedHeaders = getColumnNamesForCsv()
|
||||||
|
if (!headers.every(header => {
|
||||||
|
return expectedHeaders.indexOf(header) > -1
|
||||||
|
})) {
|
||||||
|
const msg = `Expected CSV column titles to be ${expectedHeaders.join()}`
|
||||||
|
throw new Error(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function putNullForEmptySymptoms(data) {
|
||||||
|
data.forEach(replaceWithNullIfAllPropertiesAreNull)
|
||||||
|
|
||||||
|
function replaceWithNullIfAllPropertiesAreNull(obj) {
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
if (!isObject(obj[key])) return
|
||||||
|
if (Object.values(obj[key]).every(val => val === null)) {
|
||||||
|
obj[key] = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replaceWithNullIfAllPropertiesAreNull(obj[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDbType(modelProperties, path) {
|
||||||
|
if (path.length === 1) return modelProperties[path[0]].type
|
||||||
|
const modelName = modelProperties[path[0]].objectType
|
||||||
|
return getDbType(schema[modelName], path.slice(1))
|
||||||
|
}
|
||||||
Generated
+1782
-1535
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -16,7 +16,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert": "^1.4.1",
|
"assert": "^1.4.1",
|
||||||
|
"csvtojson": "^2.0.8",
|
||||||
"date-range": "0.0.2",
|
"date-range": "0.0.2",
|
||||||
|
"isobject": "^3.0.1",
|
||||||
"js-base64": "^2.4.8",
|
"js-base64": "^2.4.8",
|
||||||
"js-joda": "^1.8.2",
|
"js-joda": "^1.8.2",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
@@ -24,16 +26,19 @@
|
|||||||
"react": "16.4.1",
|
"react": "16.4.1",
|
||||||
"react-native": "^0.56.0",
|
"react-native": "^0.56.0",
|
||||||
"react-native-calendars": "^1.19.3",
|
"react-native-calendars": "^1.19.3",
|
||||||
|
"react-native-document-picker": "^2.1.0",
|
||||||
|
"react-native-fs": "^2.10.14",
|
||||||
"react-native-modal-datetime-picker-nevo": "^4.11.0",
|
"react-native-modal-datetime-picker-nevo": "^4.11.0",
|
||||||
"react-native-share": "^1.1.0",
|
"react-native-share": "^1.1.0",
|
||||||
"react-native-simple-radio-button": "^2.7.1",
|
"react-native-simple-radio-button": "^2.7.1",
|
||||||
"react-native-svg": "^6.3.1",
|
"react-native-vector-icons": "^5.0.0",
|
||||||
"react-navigation": "^2.0.4",
|
"react-navigation": "^2.0.4",
|
||||||
"realm": "^2.7.1",
|
"realm": "^2.7.1",
|
||||||
"uuid": "^3.2.1"
|
"uuid": "^3.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/register": "^7.0.0-beta.55",
|
"@babel/register": "^7.0.0-beta.55",
|
||||||
|
"babel-eslint": "^8.2.6",
|
||||||
"babel-preset-react-native": "^5.0.0",
|
"babel-preset-react-native": "^5.0.0",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user