bitovi/canjs

View on GitHub
docs/can-guides/commitment/recipes/cta-bus-map/6-clean-markers.js

Summary

Maintainability
D
1 day
Test Coverage
import { StacheElement, type } from "//unpkg.com/can@6/core.mjs";

const proxyUrl = "https://can-cors.herokuapp.com/";
const token = "?key=piRYHjJ5D2Am39C9MxduHgRZc&format=json";
const apiRoot = "http://www.ctabustracker.com/bustime/api/v2/";
const getRoutesEnpoint = apiRoot + "getroutes" + token;
const getVehiclesEndpoint = apiRoot + "getvehicles" + token;

class GoogleMapView extends StacheElement {
  static view = `<div this:to="this.mapElement" class="gmap"></div>`;

  static props = {
    map: type.Any,
    mapElement: type.maybeConvert(HTMLElement),
    vehicles: type.Any,
    markers: type.Any
  };

  connected() {
    googleAPI.then(() => {
      this.map = new google.maps.Map(this.mapElement, {
        zoom: 10,
        center: {
          lat: 41.881,
          lng: -87.623
        }
      });
    });
    this.listenTo("vehicles", (ev, newVehicles) => {
      if (Array.isArray(this.markers)) {
        this.markers.forEach(marker => {
          marker.setMap(null);
        });
        this.markers = null;
      }
      if (newVehicles) {
        this.markers = newVehicles.map(vehicle => {
          return new google.maps.Marker({
            position: {
              lat: parseFloat(vehicle.lat),
              lng: parseFloat(vehicle.lon)
            },
            map: this.map
          });
        });
      }
    });
  }
}

customElements.define("google-map-view", GoogleMapView);

class BusTracker extends StacheElement {
  static view = `
    <div class="top">
      <div class="header">
        <h1>{{this.title}}</h1>
        {{# if(this.routesPromise.isPending) }}<p>Loading routes…</p>{{/ if }}
        {{# if(this.vehiclesPromise.isPending) }}<p>Loading vehicles…</p>{{/ if }}
      </div>
      <ul class="routes-list">
        {{# for(route of this.routesPromise.value) }}
          <li on:click="this.pickRoute(route)" {{# eq(route, this.route) }}class="active"{{/ eq }}>
            <span class="route-number">{{ route.rt }}</span>
            <span class="route-name">{{ route.rtnm }}</span>
            <span class="check">✔</span>
          </li>
        {{/ for }}
      </ul>
    </div>
    <div class="bottom">
      {{# if(this.route) }}
        <div class="route-selected" on:click="pickRoute(route)">
          <small>Route {{ this.route.rt }}:</small> {{ this.route.rtnm }}
          {{# if(this.vehiclesPromise.isRejected) }}
            <div class="error-message">No vehicles available for this route</div>
          {{/ if }}
        </div>
      {{/ if }}
      <google-map-view vehicles:from="this.vehiclesPromise.value"/>
    </div>
  `;

  static props = {
    title: {
      default: "Chicago CTA Bus Tracker"
    },

    routesPromise: {
      get default() {
        return fetch(proxyUrl + getRoutesEnpoint)
          .then(response => response.json())
          .then(data => data["bustime-response"].routes);
      }
    },

    route: type.Any,
    vehiclesPromise: type.Any
  };

  pickRoute(route) {
    this.route = route;
    this.vehiclesPromise = fetch(
      proxyUrl + getVehiclesEndpoint + "&rt=" + route.rt
    )
      .then(response => response.json())
      .then(data => {
        if (data["bustime-response"].error) {
          return Promise.reject(data["bustime-response"].error[0]);
        } else {
          return data["bustime-response"].vehicle;
        }
      });
  }
}

customElements.define("bus-tracker", BusTracker);