Skip to content
Snippets Groups Projects
settings.js 7.62 KiB
Newer Older
import React, { Component } from 'react'
import {
  View,
Julia Friesel's avatar
Julia Friesel committed
  TouchableOpacity,
  ScrollView,
Julia Friesel's avatar
Julia Friesel committed
  Text,
  Switch
} from 'react-native'
Julia Friesel's avatar
Julia Friesel committed
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
import Slider from '@ptomasroos/react-native-multi-slider'
import Share from 'react-native-share'
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
import rnfs from 'react-native-fs'
Julia Friesel's avatar
Julia Friesel committed
import styles, { secondaryColor } from '../styles/index'
import config from '../config'
Julia Friesel's avatar
Julia Friesel committed
import { settings as settingsLabels, shared as sharedLabels } from './labels'
Julia Friesel's avatar
Julia Friesel committed
import getDataAsCsvDataUri from '../lib/import-export/export-to-csv'
import importCsv from '../lib/import-export/import-from-csv'
import {
  scaleObservable,
  saveTempScale,
  tempReminderObservable,
  saveTempReminder
} from '../local-storage'

export default class Settings extends Component {
Julia Friesel's avatar
Julia Friesel committed
  constructor(props) {
    super(props)
    this.state = {}
  }

  render() {
    return (
      <ScrollView>
Julia Friesel's avatar
Julia Friesel committed
        <TouchableOpacity
          style={styles.settingsSegment}
          onPress={() => this.setState({ isTimePickerVisible: true })}
        >
Julia Friesel's avatar
Julia Friesel committed
          <Text style={styles.settingsSegmentTitle}>
            {settingsLabels.tempReminder.title}
          </Text>
          <TempReminderPicker/>
Julia Friesel's avatar
Julia Friesel committed
        </TouchableOpacity>
        <View style={styles.settingsSegment}>
Julia Friesel's avatar
Julia Friesel committed
          <Text style={styles.settingsSegmentTitle}>
Julia Friesel's avatar
Julia Friesel committed
            {settingsLabels.tempScale.segmentTitle}
Julia Friesel's avatar
Julia Friesel committed
          </Text>
Julia Friesel's avatar
Julia Friesel committed
          <Text>{settingsLabels.tempScale.segmentExplainer}</Text>
          <TempSlider/>
        </View>
Julia Friesel's avatar
Julia Friesel committed
        <View style={styles.settingsSegment}>
Julia Friesel's avatar
Julia Friesel committed
          <Text style={styles.settingsSegmentTitle}>
Julia Friesel's avatar
Julia Friesel committed
            {settingsLabels.export.button}
Julia Friesel's avatar
Julia Friesel committed
          </Text>
Julia Friesel's avatar
Julia Friesel committed
          <Text>{settingsLabels.export.segmentExplainer}</Text>
Julia Friesel's avatar
Julia Friesel committed
          <TouchableOpacity
            onPress={openShareDialogAndExport}
            style={styles.settingsButton}>
Julia Friesel's avatar
Julia Friesel committed
            <Text style={styles.settingsButtonText}>
Julia Friesel's avatar
Julia Friesel committed
              {settingsLabels.export.button}
Julia Friesel's avatar
Julia Friesel committed
            </Text>
Julia Friesel's avatar
Julia Friesel committed
          </TouchableOpacity>
        </View>
        <View style={styles.settingsSegment}>
Julia Friesel's avatar
Julia Friesel committed
          <Text style={styles.settingsSegmentTitle}>
Julia Friesel's avatar
Julia Friesel committed
            {settingsLabels.import.button}
Julia Friesel's avatar
Julia Friesel committed
          </Text>
Julia Friesel's avatar
Julia Friesel committed
          <Text>{settingsLabels.import.segmentExplainer}</Text>
Julia Friesel's avatar
Julia Friesel committed
          <TouchableOpacity
            onPress={openImportDialogAndImport}
            style={styles.settingsButton}>
Julia Friesel's avatar
Julia Friesel committed
            <Text style={styles.settingsButtonText}>
Julia Friesel's avatar
Julia Friesel committed
              {settingsLabels.import.button}
Julia Friesel's avatar
Julia Friesel committed
            </Text>
Julia Friesel's avatar
Julia Friesel committed
          </TouchableOpacity>
class TempReminderPicker extends Component {
  constructor(props) {
    super(props)
    this.state = Object.assign({}, tempReminderObservable.value)
  }

  render() {
    return (
      <View style={{ flexDirection: 'row', alignItems: 'center' }}>
        <View style={{ flex: 1 }}>
          {this.state.time && this.state.enabled ?
            <Text>{settingsLabels.tempReminder.timeSet(this.state.time)}</Text>
            :
            <Text>{settingsLabels.tempReminder.noTimeSet}</Text>
          }
        </View>
        <Switch
          value={this.state.enabled}
          onValueChange={val => {
            this.setState({ enabled: val })
            if (val && !this.state.time) this.setState({ isTimePickerVisible: true })
            if (!val) saveTempReminder({ enabled: false })
          }}
          onTintColor={secondaryColor}
        />
        <DateTimePicker
          mode="time"
          isVisible={this.state.isTimePickerVisible}
          onConfirm={jsDate => {
            const time = padWithZeros(`${jsDate.getHours()}:${jsDate.getMinutes()}`)
            this.setState({
              time,
              isTimePickerVisible: false
            })
            saveTempReminder({
              time,
              enabled: true
            })
          }}
Julia Friesel's avatar
Julia Friesel committed
          onCancel={() => {
            this.setState({ isTimePickerVisible: false })
            if (!this.state.time) this.setState({enabled: false})
          }}
class TempSlider extends Component {
  constructor(props) {
    super(props)
    this.state = Object.assign({}, scaleObservable.value)
  }

  onValuesChange = (values) => {
    this.setState({
      min: values[0],
      max: values[1]
    })
  }

  onValuesChangeFinish = (values) => {
    this.setState({
      min: values[0],
      max: values[1]
    })
    try {
      saveTempScale(this.state)
    } catch(err) {
Julia Friesel's avatar
Julia Friesel committed
      alertError(settingsLabels.tempScale.saveError)
Julia Friesel's avatar
Julia Friesel committed
      <View style={{alignItems: 'center'}}>
Julia Friesel's avatar
Julia Friesel committed
        <Text>{`${settingsLabels.tempScale.min} ${this.state.min}`}</Text>
        <Text>{`${settingsLabels.tempScale.max} ${this.state.max}`}</Text>
        <Slider
          values={[this.state.min, this.state.max]}
          min={config.temperatureScale.min}
          max={config.temperatureScale.max}
          step={0.5}
          onValuesChange={this.onValuesChange}
          onValuesChangeFinish={this.onValuesChangeFinish}
Julia Friesel's avatar
Julia Friesel committed
          selectedStyle={{
            backgroundColor: 'darkgrey',
          }}
          unselectedStyle={{
            backgroundColor: 'silver',
          }}
          trackStyle={{
            height:10,
          }}
          markerStyle={{
Julia Friesel's avatar
Julia Friesel committed
            backgroundColor: secondaryColor,
Julia Friesel's avatar
Julia Friesel committed
            height: 20,
            width: 20,
            borderRadius: 100,
            marginTop: 10
          }}
Julia Friesel's avatar
Julia Friesel committed
async function openShareDialogAndExport() {
  let data
  try {
    data = getDataAsCsvDataUri()
    if (!data) {
Julia Friesel's avatar
Julia Friesel committed
      return alertError(settingsLabels.errors.noData)
Julia Friesel's avatar
Julia Friesel committed
    }
  } catch (err) {
    console.error(err)
Julia Friesel's avatar
Julia Friesel committed
    return alertError(settingsLabels.errors.couldNotConvert)
Julia Friesel's avatar
Julia Friesel committed
  }

  try {
    await Share.open({
Julia Friesel's avatar
Julia Friesel committed
      title: settingsLabels.export.title,
Julia Friesel's avatar
Julia Friesel committed
      url: data,
Julia Friesel's avatar
Julia Friesel committed
      subject: settingsLabels.export.subject,
Julia Friesel's avatar
Julia Friesel committed
      type: 'text/csv',
      showAppsToView: true
    })
  } catch (err) {
    console.error(err)
Julia Friesel's avatar
Julia Friesel committed
    return alertError(settingsLabels.export.errors.problemSharing)
Julia Friesel's avatar
Julia Friesel committed
function openImportDialogAndImport() {
  Alert.alert(
Julia Friesel's avatar
Julia Friesel committed
    settingsLabels.import.title,
    settingsLabels.import.message,
Julia Friesel's avatar
Julia Friesel committed
      text: settingsLabels.import.replaceOption,
Julia Friesel's avatar
Julia Friesel committed
      onPress: () => getFileContentAndImport({ deleteExisting: false })
    }, {
Julia Friesel's avatar
Julia Friesel committed
      text: settingsLabels.import.deleteOption,
Julia Friesel's avatar
Julia Friesel committed
      onPress: () => getFileContentAndImport({ deleteExisting: true })
    }, {
Julia Friesel's avatar
Julia Friesel committed
      text: sharedLabels.cancel, style: 'cancel', onPress: () => { }
Julia Friesel's avatar
Julia Friesel committed
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) {
Julia Friesel's avatar
Julia Friesel committed
    return importError(settingsLabels.import.errors.couldNotOpenFile)
Julia Friesel's avatar
Julia Friesel committed
    await importCsv(fileContent, deleteExisting)
Julia Friesel's avatar
Julia Friesel committed
    Alert.alert(sharedLabels.successTitle, settingsLabels.import.success.message)
  } catch(err) {
    importError(err.message)
Julia Friesel's avatar
Julia Friesel committed
  Alert.alert(sharedLabels.errorTitle, msg)
}

function importError(msg) {
Julia Friesel's avatar
Julia Friesel committed
  const postFixed = `${msg}\n\n${settingsLabels.import.errors.postFix}`
  alertError(postFixed)
Julia Friesel's avatar
Julia Friesel committed

function padWithZeros(time) {
  const vals = time.split(':')
  return vals.map(val => {
    if (parseInt(val) < 10) {
      val = `0${val}`
    }
    return val
  }).join(':')
}