Skip to content
Snippets Groups Projects
Commit 7ea6a683 authored by Julia Friesel's avatar Julia Friesel
Browse files

Merge branch '113-add-button-to-export-to-settings-page' into 'master'

Resolve "Add button to export to settings page"

Closes #113

See merge request bloodyhealth/drip!46
parents 4b971a1f 355bcbe8
No related branches found
No related tags found
No related merge requests found
...@@ -137,6 +137,7 @@ android { ...@@ -137,6 +137,7 @@ android {
} }
dependencies { dependencies {
compile project(':react-native-share')
compile project(':realm') compile project(':realm')
compile project(':react-native-svg') compile project(':react-native-svg')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
......
...@@ -21,6 +21,16 @@ ...@@ -21,6 +21,16 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.drip.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application> </application>
</manifest> </manifest>
...@@ -3,6 +3,8 @@ package com.drip; ...@@ -3,6 +3,8 @@ package com.drip;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import cl.json.RNSharePackage;
import cl.json.ShareApplication;
import io.realm.react.RealmReactPackage; import io.realm.react.RealmReactPackage;
import com.horcrux.svg.SvgPackage; import com.horcrux.svg.SvgPackage;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
...@@ -13,7 +15,7 @@ import com.facebook.soloader.SoLoader; ...@@ -13,7 +15,7 @@ import com.facebook.soloader.SoLoader;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class MainApplication extends Application implements ReactApplication { public class MainApplication extends Application implements ReactApplication, ShareApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override @Override
...@@ -25,6 +27,7 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -25,6 +27,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new RNSharePackage(),
new RealmReactPackage(), new RealmReactPackage(),
new SvgPackage() new SvgPackage()
); );
...@@ -46,4 +49,9 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -46,4 +49,9 @@ public class MainApplication extends Application implements ReactApplication {
super.onCreate(); super.onCreate();
SoLoader.init(this, /* native exopackage */ false); SoLoader.init(this, /* native exopackage */ false);
} }
@Override
public String getFileProviderAuthority() {
return "com.drip.provider";
}
} }
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="Downloads" path="Download/" />
</paths>
\ No newline at end of file
rootProject.name = 'drip' rootProject.name = 'drip'
include ':react-native-share'
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' include ':react-native-svg'
......
...@@ -4,8 +4,10 @@ import Home from './components/home' ...@@ -4,8 +4,10 @@ import Home from './components/home'
import Calendar from './components/calendar' import Calendar from './components/calendar'
import CycleDay from './components/cycle-day' import CycleDay from './components/cycle-day'
import Chart from './components/chart/chart' import Chart from './components/chart/chart'
import Settings from './components/settings'
// this is until react native fixes this bug, see https://github.com/facebook/react-native/issues/18868#issuecomment-382671739 // this is until react native fixes this bugg, see
// 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'])
...@@ -13,5 +15,6 @@ export default createStackNavigator({ ...@@ -13,5 +15,6 @@ export default createStackNavigator({
home: { screen: Home }, home: { screen: Home },
calendar: { screen: Calendar }, calendar: { screen: Calendar },
cycleDay: { screen: CycleDay }, cycleDay: { screen: CycleDay },
chart: { screen: Chart } chart: { screen: Chart },
settings: { screen: Settings }
}) })
...@@ -69,6 +69,12 @@ export default class Home extends Component { ...@@ -69,6 +69,12 @@ export default class Home extends Component {
title="Go to chart"> title="Go to chart">
</Button> </Button>
</View> </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()}
......
export const settings = {
errors: {
noData: 'There is no data to export',
couldNotConvert: 'Could not convert data to CSV',
problemSharing: 'There was a problem sharing the data export file'
},
exportTitle: 'My Drip data export',
exportSubject: 'My Drip data export',
buttonLabel: 'Export data'
}
\ No newline at end of file
import React, { Component } from 'react'
import {
View,
Button,
Text,
ScrollView,
Alert
} from 'react-native'
import Share from 'react-native-share'
import getDataAsCsvDataUri from '../lib/export-to-csv'
import styles from '../styles/index'
import { settings as labels } from './labels'
export default class Settings extends Component {
constructor(props) {
super(props)
this.state = {
pickerVisible: false
}
}
render() {
return (
<ScrollView>
<Text style={styles.welcome}>{this.state.welcomeText}</Text>
<View style={styles.homeButtons}>
<View style={styles.homeButton}>
<Button
onPress={async () => {
let data
try {
data = getDataAsCsvDataUri()
if (!data) {
return Alert.alert(labels.errors.noData)
}
} catch (err) {
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>
</View>
</View>
</ScrollView>
)
}
}
\ No newline at end of file
...@@ -110,6 +110,7 @@ const db = new Realm(realmConfig) ...@@ -110,6 +110,7 @@ const db = new Realm(realmConfig)
const bleedingDaysSortedByDate = db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) const bleedingDaysSortedByDate = db.objects('CycleDay').filtered('bleeding != null').sorted('date', true)
const temperatureDaysSortedByDate = db.objects('CycleDay').filtered('temperature != null').sorted('date', true) const temperatureDaysSortedByDate = db.objects('CycleDay').filtered('temperature != null').sorted('date', true)
const cycleDaysSortedByDate = db.objects('CycleDay').sorted('date', true)
function saveSymptom(symptom, cycleDay, val) { function saveSymptom(symptom, cycleDay, val) {
db.write(() => { db.write(() => {
...@@ -117,8 +118,6 @@ function saveSymptom(symptom, cycleDay, val) { ...@@ -117,8 +118,6 @@ function saveSymptom(symptom, cycleDay, val) {
}) })
} }
const cycleDaysSortedByDate = db.objects('CycleDay').sorted('date', true)
function getOrCreateCycleDay(localDate) { function getOrCreateCycleDay(localDate) {
let result = db.objectForPrimaryKey('CycleDay', localDate) let result = db.objectForPrimaryKey('CycleDay', localDate)
if (!result) { if (!result) {
...@@ -176,6 +175,24 @@ function getPreviousTemperature(cycleDay) { ...@@ -176,6 +175,24 @@ function getPreviousTemperature(cycleDay) {
return winner.temperature.value return winner.temperature.value
} }
function getColumnNamesForCsv() {
return getPrefixedKeys('CycleDay')
function getPrefixedKeys(schemaName, prefix) {
const schema = db.schema.find(x => x.name === schemaName).properties
return Object.keys(schema).reduce((acc, key) => {
const prefixedKey = prefix ? [prefix, key].join('.') : key
const childSchemaName = schema[key].objectType
if (!childSchemaName) {
acc.push(prefixedKey)
return acc
}
acc.push(...getPrefixedKeys(childSchemaName, prefixedKey))
return acc
}, [])
}
}
export { export {
saveSymptom, saveSymptom,
getOrCreateCycleDay, getOrCreateCycleDay,
...@@ -185,5 +202,6 @@ export { ...@@ -185,5 +202,6 @@ export {
fillWithDummyData, fillWithDummyData,
deleteAll, deleteAll,
getPreviousTemperature, getPreviousTemperature,
getCycleDay getCycleDay,
getColumnNamesForCsv
} }
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
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 */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
...@@ -354,6 +355,8 @@ ...@@ -354,6 +355,8 @@
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; };
A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */ = {isa = PBXFileReference; name = "libRNShare.a"; path = "libRNShare.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -386,6 +389,7 @@ ...@@ -386,6 +389,7 @@
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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -577,6 +581,7 @@ ...@@ -577,6 +581,7 @@
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */, 8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */,
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */, 7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */,
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */,
); );
name = Libraries; name = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1203,11 +1208,13 @@ ...@@ -1203,11 +1208,13 @@
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/drip.app/drip"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/drip.app/drip";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Debug; name = Debug;
...@@ -1228,11 +1235,13 @@ ...@@ -1228,11 +1235,13 @@
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/drip.app/drip"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/drip.app/drip";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Release; name = Release;
...@@ -1256,6 +1265,7 @@ ...@@ -1256,6 +1265,7 @@
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Debug; name = Debug;
...@@ -1278,6 +1288,7 @@ ...@@ -1278,6 +1288,7 @@
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Release; name = Release;
...@@ -1307,11 +1318,13 @@ ...@@ -1307,11 +1318,13 @@
TVOS_DEPLOYMENT_TARGET = 9.2; TVOS_DEPLOYMENT_TARGET = 9.2;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Debug; name = Debug;
...@@ -1341,11 +1354,13 @@ ...@@ -1341,11 +1354,13 @@
TVOS_DEPLOYMENT_TARGET = 9.2; TVOS_DEPLOYMENT_TARGET = 9.2;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Release; name = Release;
...@@ -1374,11 +1389,13 @@ ...@@ -1374,11 +1389,13 @@
TVOS_DEPLOYMENT_TARGET = 10.1; TVOS_DEPLOYMENT_TARGET = 10.1;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Debug; name = Debug;
...@@ -1407,11 +1424,13 @@ ...@@ -1407,11 +1424,13 @@
TVOS_DEPLOYMENT_TARGET = 10.1; TVOS_DEPLOYMENT_TARGET = 10.1;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios",
); );
}; };
name = Release; name = Release;
......
import objectPath from 'object-path'
import { Base64 } from 'js-base64'
import { getColumnNamesForCsv, cycleDaysSortedByDate } from '../db'
export default function makeDataURI() {
if (!cycleDaysSortedByDate.length) return null
const csv = transformToCsv(cycleDaysSortedByDate)
const encoded = Base64.encodeURI(csv)
return `data:text/csv;base64,${encoded}`
}
function transformToCsv(cycleDays) {
const columnNames = getColumnNamesForCsv()
const rows = cycleDays
.map(day => {
return columnNames.map(column => {
const val = objectPath.get(day, column)
return typeof val === 'string' ? csvify(val) : val
})
})
.map(row => row.join(','))
rows.unshift(columnNames.join(','))
return rows.join('\n')
}
function csvify (val) {
// we wrap fields with special characters in quotes,
// thus have to escape actual quotes
val = val.replace(/"/g, '""')
val = val.toLowerCase()
const hasSpecialChars = (
val.includes('\n') ||
val.includes('\t') ||
val.includes(',') ||
val.includes(';') ||
val.includes('.') ||
val.includes('\'')
)
return hasSpecialChars ? `"${val}"` : val
}
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment