diff --git a/components/settings.js b/components/settings.js index cfce6dac809ee4cea0b89a83d5f3eb164a5bd7c7..13ac3204c34e29fb943a1ef254d37f6f2650364f 100644 --- a/components/settings.js +++ b/components/settings.js @@ -3,12 +3,11 @@ import { View, Button, Text, - ScrollView + ScrollView, + Alert } from 'react-native' import Share from 'react-native-share' -import { Base64 } from 'js-base64' -import objectPath from 'object-path' -import { getColumnNamesForCsv, cycleDaysSortedByDate } from '../db' +import getDataAsCsvDataUri from '../lib/export-to-csv' import styles from '../styles/index' export default class Settings extends Component { @@ -28,9 +27,17 @@ export default class Settings extends Component { <View style={styles.homeButton}> <Button onPress={async () => { - // TODO show warning that there is nothing to export - if (!cycleDaysSortedByDate.length) return - const data = makeDataURI(cycleDaysSortedByDate) + let data + try { + data = getDataAsCsvDataUri() + if (!data) { + return Alert.alert('There is no data to export') + } + } catch (err) { + console.error(err) + return Alert.alert('Could not convert data to CSV') + } + try { await Share.open({ title: 'My Drip data export', @@ -40,53 +47,15 @@ export default class Settings extends Component { showAppsToView: true }) } catch (err) { - // TODO handle error - console.log(err) + console.error(err) + return Alert.alert('There was a problem sharing the data export file') } }} - title="Edit symptoms for today"> + title="Export data"> </Button> </View> </View> </ScrollView> ) } -} - -function makeDataURI(cycleDays) { - const csv = transformToCsv(cycleDays) - 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) { - // escape double 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 diff --git a/lib/export-to-csv.js b/lib/export-to-csv.js new file mode 100644 index 0000000000000000000000000000000000000000..56bc4c5186f5d135ae1888d5c312208389e0668c --- /dev/null +++ b/lib/export-to-csv.js @@ -0,0 +1,45 @@ +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