conveyal/modeify

View on GitHub
client/locations-view/index.js

Summary

Maintainability
B
6 hrs
Test Coverage
var analytics = require('../analytics')
var closest = require('component-closest')
var log = require('../log')('locations-view')
var textModal = require('../text-modal')
var view = require('../view')
var LocationSuggest = require('../location-suggest')
var extend = require('../components/segmentio/extend/1.0.0')
var session = require('../session')

/**
 * Expose `View`
 */

var View = module.exports = view(require('./template.html'), function (view, plan) {
  plan.on('change', function (name) {
    view.resetIcons()

    if (name === 'from') view.find('#from-location').value = plan.from()
    if (name === 'to') view.find('#to-location').value = plan.to()

    if (session.user() && (name === 'from' || name === 'to')) {
      view.checkAddressFavorite(name)
    }
  })

  view.on('rendered', function () {
    // Reset the icons
    view.resetIcons()

    // Set the initial state of the favorite icons
    if (session.user()) {
      view.checkAddressFavorites()
    }

    // On form submission
    closest(view.el, 'form').onsubmit = function (e) {
      e.preventDefault()

      // only reset addresses if needed
      const newFromValue = view.find('.from input').value
      const newToValue = view.find('.to input').value

      if (newFromValue !== plan.from() && newToValue !== plan.to()) {
        plan.setAddresses(newFromValue, newToValue, function (err) {
          if (err) {
            log.error('%e', err)
          } else {
            plan.updateRoutes()
          }
        })
      } else if (newFromValue !== plan.from()) {
        plan.setAddress('from', newFromValue, function (err) {
          if (err) {
            log.error('%e', err)
          } else {
            plan.updateRoutes()
          }
        })
      } else if (newToValue !== plan.to()) {
        plan.setAddress('to', newToValue, function (err) {
          if (err) {
            log.error('%e', err)
          } else {
            plan.updateRoutes()
          }
        })
      }
    }
  })

  function listenToUserForFavoriteChanges () {
    session.user().on('change user_metadata', () => {
      view.checkAddressFavorites()
    })
  }

  session.on('change isLoggedIn', () => {
    if (session.user()) {
      view.checkAddressFavorites()
      listenToUserForFavoriteChanges()
    }
  })

  if (session.user()) {
    listenToUserForFavoriteChanges()
  }
})

extend(View.prototype, LocationSuggest.prototype)

/**
 * Show clear or current location, but not both
 */

View.prototype.resetIcons = function (e) {
  showClearOrCurrentLocation(this, 'from')
  showClearOrCurrentLocation(this, 'to')

  function showClearOrCurrentLocation (view, name) {
    var selector = '.' + name
    var value = view.find(selector + ' input').value
    var refresh = view.find(selector + ' .findingCurrentLocation')
    var clear = view.find(selector + ' .clear')
    var location = view.find(selector + ' .currentLocation')

    refresh.classList.add('hidden')

    if (!value || !value.trim || value.trim().length === 0) {
      clear.classList.add('hidden')
      location.classList.remove('hidden')
    } else {
      clear.classList.remove('hidden')
      location.classList.add('hidden')
    }
  }
}

/**
 * Use the current location if it's available
 */

View.prototype.currentLocation = function (e) {
  e.preventDefault()
  if ('geolocation' in navigator) {
    var name = e.target.parentNode.classList.contains('from') ? 'from' : 'to'
    var input = this.find('.' + name + ' input')
    var self = this

    e.target.classList.add('hidden')
    this.find('.' + name + ' .findingCurrentLocation').classList.remove('hidden')

    navigator.geolocation.getCurrentPosition(function (position) {
      var c = position.coords
      input.value = c.longitude + ', ' + c.latitude
      self.save(input)
    }, function (err) {
      console.error(err)
      self.resetIcons()
      window.alert('Whoops! We were unable to find your current location.')
    }, {
      enableHighAccuracy: true, // use GPS if available
      maximumAge: 60000, // 60 seconds
      timeout: 30000 // 30 seconds
    })
  } else {
    window.alert('Whoops! Looks like GPS location not available on this device.')
  }
}

View.prototype.locationSelected = function (target, val, magicKey) {
  this.save(target, val, magicKey)
}

/**
 * Geocode && Save
 */

View.prototype.save = function (el, val, magicKey) {
  var plan = this.model
  var name = el.name
  val = val || el.value

  if (val && plan[name]() !== val) {
    analytics.track('Location Found', {
      address: val,
      type: name
    })
    let locationData = val
    if (magicKey) {
      locationData = {
        address: val,
        magicKey
      }
    }
    this.model.setAddress(name, locationData, function (err, location) {
      if (err) {
        log.error('%e', err)
        textModal('Invalid address.')
      } else if (location) {
        plan.updateRoutes()
      }
    })
  }

  this.resetIcons()
}

/**
 * Highlight the selected input
 */

View.prototype.focusInput = function (e) {
  e.target.parentNode.classList.add('highlight')
}

/**
 * Clear
 */

View.prototype.clear = function (e) {
  e.preventDefault()
  var inputGroup = e.target.parentNode
  var input = inputGroup.getElementsByTagName('input')[0]
  input.value = ''
  input.focus()

  this.resetIcons()
}

View.prototype.toggleFavorite = function (e) {
  if (!session.user()) {
    // TODO: encourage user to register?
    return
  }

  var type = e.target.parentNode.classList.contains('from') ? 'from' : 'to'
  var address = this.model.get(type)
  const gps = this.model.get(`${type}_ll`)

  if (e.target.classList.contains('fa-heart-o')) {
    session.user().addFavoritePlace({
      address,
      lat: gps.lat,
      lon: gps.lng
    })
    session.user().saveUserMetadata(function () {})
    this.checkAddressFavorite(type)
  } else {
    session.user().deleteFavoritePlace(address)
    session.user().saveUserMetadata(function () {})
    this.checkAddressFavorite(type)
  }
}

View.prototype.checkAddressFavorites = function () {
  this.checkAddressFavorite('from')
  this.checkAddressFavorite('to')
}

View.prototype.checkAddressFavorite = function (type) {
  var el = this.find('.' + type + '-favorite')
  if (session.user().isFavoritePlace(this.model.get(type))) {
    enableFavoriteIcon(el)
    el.title = 'Added to favorite places'
  } else {
    disableFavoriteIcon(el)
    el.title = 'Add to favorite places'
  }
}

function enableFavoriteIcon (el) {
  el.classList.remove('fa-heart-o')
  el.classList.add('fa-heart')
}

function disableFavoriteIcon (el) {
  el.classList.remove('fa-heart')
  el.classList.add('fa-heart-o')
}