HustleTime Development: F express in Brooklyn

It’s been a few weeks since I last posted to the blog, and I wanted to let you know that I’m still here and working on HustleTime (on Ios anyway).

I’ve decided to abandon Android development for the time being, as the react-native-maps library is woefully out of date, and now only community supported. There are some strange things happening in Android that have caused me too much grief to continue to spin my wheels on it.

If HustleTime takes off on Ios, I’ll consider revamping and retooling the Android version, but for now, I’m sticking with Ios.

F Express

The MTA in its infinite wisdom has decided to add 2 express F trains in Brooklyn every morning (just two!) So in the next update to HustleTime, you’ll be able to see those trains and ignore them in the app.

I’ve also added a timeout to more of the gps functions in the app, so if you’re not able to get gps coordinates on the first function call, you should now be able to do that again. The code looks like this:

  getLocation = () => {
    navigator.geolocation.getCurrentPosition(
      currentPosition => {
        this.props.dispatchedCircleCenter({
          latitude: currentPosition.coords.latitude,
          longitude:currentPosition.coords.longitude})}, 
      error => null,
      { timeout: 2000 }
    )
  }

getCurrentPosition() takes 3 arguments, the third being a timeout, which I had conspicuously omitted from prior versions. This would explain strange behavior when underground (for example) when you couldn’t get your location, nor could you update your position if you connected to a network.

HustleTime Development: A (Slightly) New Look

After having time to have a sit down with my good friend Andy Schedler to talk about Ui, I’ve implemented some small changes to make HustleTime more intuitive and clearer for users.

I’ve moved the recentering button to the bottom right, and replaced the chevrons or menu button with sliders to indicate a “settings” menu.

I’ve modified the “you are here” icon to reflect the iconography more suitably, and changed some of the iconography in the modal, by changing from gray and black to adding some color so users more intuitively know where to click.

I’ve also completely removed the “close” button on the modal and instead created an “x” in the top right to close the modal, which lines up with where the menu button is to begin with (the modal no longer covers up the ad).

I’ve also made some functional upgrades:

Now, if you aren’t near a station, you’ll see an alert, letting you know you don’t have a station nearby. This could be very helpful for someone opening the app for the first time, so they know what to do, and understand how the app behaves.

I’ve also fixed an issue that would arise if someone wasn’t able to geolocate. This issue could arise if you’re underground and unable to get a location.

  getLocation = () => {
    navigator.geolocation.getCurrentPosition(
      currentPosition => {
  	this.props.dispatchedNewCenter({
          latitude: currentPosition.coords.latitude,
          longitude:currentPosition.coords.longitude
        }),
      this.props.dispatchedCircleCenter({
        latitude: currentPosition.coords.latitude,
        longitude:currentPosition.coords.longitude})
      }, error => {
        this.setDefaultLocation()
      },{timeout: 2000}
    )
  .then( x => this.props.dispatchedAppReady({located: true}))
}

There’s a third prop for navigator.geolocation.getCurrentPosition, and that’s the timeout. Without explicitly setting that timeout, navigator.geolocation.getCurrentPosition gets lost in an unresolved promise, and you’re unable to recenter the map, or find a new location. Now by adding a 2-second timeout I restore that functionality, should you initially not have the ability to geolocate due to being underground or lacking some type of internet service.

Next big changes?

I’m going to bring the android version up to date with Ios, and then work on some back-end changes, specifically: adding queuing so the server can handle more requests at the same time, and implementing the new F express service, which starts next month.

You can get HustleTime on both Android and iPhone free!

HustleTime Development: New Feature and bug fixes

Now that I’m back working on my mac, and an xCode that seems to be far more responsive to the machinations of ReactNative than it’s counterpart, Android Studio, I can get back to work.

One of the things I’ve done is now completely implements the 2 versions of the <ArrivalNotification />

This is going to be very useful to all the folks who need to see the last stop on a train. It’s a work in progress, however, as I need to continue to look for oddball train terminals that aren’t part of the default set of stations. What I mean, is that if a train ending service at some random station, HustleTime might not know what to do with it, until I add that station to the list of possible terminal stations.

It’s an imperfect system, but essentially it derives from the fact the MTA GTFS doesn’t include the last stop field, and instead, I’m reduced to some heavy lifting as hustletime-api sorts through all the stops in a current route and then gets the last entry in that array, stores it to a variable, and then includes that in the JSON for each arrival.

I’ve added a little searching indicator so you see when HustleTime is looking for data from the server . This is helpful when you’re underground, and/or don’t have a great signal, so you’ll see that HustleTime is working to get you up-to-the-minute info for arrivals.

I’ve also gone into the code and tried to eliminate some unnecessary API calls to the server. This is mainly accomplished by setting redux store to have an additional key called appReady.

  appReady: {
    featuresLoaded: false,
    mapLoaded: false,
    located: false,
  }

as parts of the app load, it will hit the reducer and change the values in the store, only when all 3 are true, will the app load data from the MTA. One disadvantage of this is that Location Services do indeed need to be enabled for the app to function, where in the past, I had set a default location. My next step is to set a default location if indeed location services are denied.

That involves adding a callback function to the second field in the navigator.geolocation.getCurrentPosition function.

getLocation = () => {
  navigator.geolocation.getCurrentPosition(currentPosition => {
    this.props.dispatchedNewCenter({
      latitude: currentPosition.coords.latitude,
      longitude:currentPosition.coords.longitude}),
    this.props.dispatchedCircleCenter({
      latitude: currentPosition.coords.latitude,
      longitude:currentPosition.coords.longitude})
    }, error => {
      this.setDefaultLocation()
    }
  )
  .then( x => this.props.dispatchedAppReady({located: true}))
}

setDefaultLocation = () => {
  this.props.dispatchedNewCenter({
    latitude: 40.6931111,
    longitude: -73.9913111}),
  this.props.dispatchedCircleCenter({
    latitude: 40.6931111,
    longitude: -73.9913111})
}

When Ios rejects the location request due to a permission not being enabled, it’ll call the setDefaultLocation function, and set lat and lng for somewhere in Boerum Hill.

I’m excited for this latest version of HustleTime, and I’m fairly confident that this will be the last real round of functional updates before I get into the two next big pieces:

  1. refactoring code for readability and efficiency
  2. cosmetic upgrades

Look for this new version (2.1) on the AppStore in the next few days!

as always,
You can get HustleTime on both Android and iPhone free!

HustleTime Development: Delays on Android

With so many of my friends being Android users, I had aimed to keep Android and Ios released in sync, so that all features were available concurrently.

But it’s been 3 weeks since my last post here, and that’s because Android, as usual, is giving me a ton of problems, and I’ve decided not to move forward with new Android releases until I can finish out the build on Ios.

The problems are two-fold. One stems from a strange issue I’m having with react-native-maps library and the component hierarchy, the other has to do with how react-native-maps handles displaying markers over the map.

The <MapView> is particularly finicky, and renders on top of everything, except for components in the same level. For example:

<MapView
  style={{zIndex: -1}}
/>
<Marker
  style={{zIndex: 1}}
/>

works fine, rendering the Marker over the map. The larger issue is when I am rending within HustleTime’s hierarchy, which looks like this:

App<<Home<<MapContainer<<Markers

In this structure, <MapContainer/> renders on top of everything rendered in <App/> and <Home/>, and since zIndex only cares about elements in the same file, and because <App/> and <Home/> both render things to the screen (alerts and buttons respectively) I’ve needed to compress the entire Home component into <MapContainer/>, and move a number of functions from <App/> into <MapContainer/>. This is bad.

My theory is that with this structure, when the <MapContainer/> is re-rendering, it’s doing a lot more work, and it’s slowing things down significantly.

Another major problem I’m having is with the <DefaultMarker /> component. <DefaultMarker /> displays smaller, grayed out markers over stations outside your search radius.

On the bottom right of the screen you can see several stations on the map, giving a user a guide so that they can move the map around to other stations. This is a fairly important feature, and for some reason, in Android, rendering these 497 markers can take up to 3 minutes. That’s unacceptable.

I’ve managed to prevent them from having to re-render, but the initial render is a major lag, and it’s preventing the app from working properly.

There’s a larger solution that involves some math and the map, and rendering only those markers that are viewable on the screen, but that solution is also rife with issues, I’m afraid that when the map is dragged, there won’t be markers to home in on.

So, considering these major issues, until I can figure out what the hell is going on, I’m going to have to put the Android implementation on hold.

There’s a basic working app there now (though it’s got its problems) and I just can’t spin my wheels trying to figure out Android right now. If anyone is willing to help, I’d gladly take it.

Thanks, and sorry android users.

Download HustleTime on android or iPhone free!

HustleTime Development: Final Destination

Something that folks using HustleTime have asked for is a way to see the destination of each train that’s arriving.

The MTA provides this data on their station arrival boards, they often read something like this:

1. (G) Church Ave          4min
2. (F) Coney Island        8min
3. (G) Court Square        9min
4. (F) Jamaica 179 Street  10min
5. (F) Kings Hwy           12min

This is actually a useful bit of information, and it brings me to question how I organize HustleTime’s ArrivalNotifications.

There are a couple of issues here:
1) Even though the MTA gives out information in terms of North and South, human beings don’t think that way. Straphangers generally think in terms of Uptown and Downtown, or even Manhattan bound, Queens bound, etc.
2) I’m limited (figuratively) by real estate. I don’t have a whole lot of room in this format to give users more information.

So what I’ve decided I’m going to do is organize this as option. I’ll let users choose if they want the “compact” (i.e. current) version of the Arrival Notification or let them see a more detailed “to be named” notification, and I’m currently debating between 2 layouts:

There are pluses and minuses to both layouts… What I like about the top one is that the timetable is intuitive, you’re seeing the departures in order, regardless of their destination. It is possible in this view, however to show only incoming uptown trains, or in very busy stations (like West4th or Atlantic avenue’s Pacific street platform) to not see particular trains at all, even when ignoring.

The bottom ArrivalNotification separates out Northbound and Southbound departures, showing the next 2 in either direction, so addressing the issues I have with the top notification.

It’s also worth noting that both of these options will infringe on the map space, so I may need to work out how much less of the map will remain visible with the larger view enabled.

Regardless of the Ui I choose to implement, I need to set up some things on the back end to allow for this information to passed to the front end.

GTFS

GTFS is particularly inextricable to me, and according to GTFS documentation trips should have a key called stop_headsign that “appears on signage identifying the trip’s destination to riders.”

But this is the MTA, and there’s no such key in the feeds, so I need to figure out how the MTA actually gets that information on their signs.

When a feed is decoded there’s an entity called :stop_time_update that contains what’s essentially an array of all the information for a particular train along its route.

if I take the last element of this array and assign it to a variable, I can preserve this station as I iterate through each element in that array, where I pull out all the actual departure data:

last_stop = entity.trip_update.stop_time_update.last[:stop_id]

.
.
.
  for entity in lineFeed.entity do
    last_stop = entity.trip_update.stop_time_update.last[:stop_id]
    for upd in entity.trip_update.stop_time_update do
      if upd.departure
        if upd.departure.time && upd.departure.time > (timeNow + 5)
          oneArrival = {train: entity.trip_update.to_hash[:trip][:route_id], time: upd.departure.time, station: upd.stop_id, destination: last_stop}
          data_array.push(oneArrival)
        end
      end
.
.
.

Basically what I’m doing here is hanging on to the last stop of each :trip_update as I iterate through and add it into the arrivals as a key called destination

Now with the Redis data containing the final stops for each arrival, I can do the business of sending that to the front end and render SOMETHING in the app.

I’ll get to that later.

You can get HustleTime on both Android and iPhone free!

HustleTime Development: A tiny improvement

Sometimes I’m underground and there isn’t an arrivals board in sight, or it’s far down the other end of the platform or whatever the case, and I just can’t see it.

I open HustleTime, and it hiccups – I don’t have a strong connection, and it’s still retrieving data from the HustleTime API.

This is an unavoidable condition of urban subterranean travel: you’ll sometimes have a weak or no signal, and you really can’t do anything about it.

What I’ve decided to do is to indicate to the user that they’re device is still communicating by an animated icon in the top left of the screen.

searching…

The idea is simple: While the app is inside the fetch promise of retrieving data, this little alert turns on temporarily, and once it’s done retrieving data, it turns off the alert.

this.setState({searchingIndicator: true})
      fetch(<fetch stuff>)
        .then(resp => resp.json())
        .then(json => this.props.dispatchedFetchStations(json))
        .then(resp => this.setState({searchingIndicator: false}))

.
.
.

  showAlerts = () => {
    if (this.state.searchingIndicator == true){
      if (Object.values(this.state.shown).includes(false)) {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={wifiRed} style={{width: 25, height: 25}} />
          <Image source={ignoreAlert} style={{width: 25, height: 25 }} />
        </View>
      } else {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={wifiRed} style={{width: 25, height: 25}} />
        </View>
      }
    } else {
      if (Object.values(this.state.shown).includes(false)) {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={ignoreAlert} style={{width: 25, height: 25 }} />
        </View>
      } else {
        null
      }
    }
  }

Now I have a way to show various indicators/alerts to the user as needed, and it’s a very simple process.

Look out for this little update in the next HT release.

Download HustleTime on android or iPhone free!

HustleTime Development: Status page (api)

I created the HustleTime back end with a rails api, and when I actually deployed it to Digital Ocean, I left some of the wrong settings in place and I ended up with this:

What the HELL is this?

That was what was on hustletime.paulbomba.com if you happened to look there… it’s a long list of pregenerated routes that don’t really lead anywhere. They are routes that are vestiges of prior versions of code, and I never went in and cleaned things up.

I also thought it might be helpful to have a view at that address. I wanted to have something there in case someone visited the URL.

I decided that I’d put a status page up, where you can see the current seed status of HustleTime’s data.

In order to do that, I had to do a couple of things.

a- create a view
b- put the assets together
c- clean up the routes
d- create a route for the new page
e-figure out how to collect and store latest seed information

One of the things about rails api is that each controller has inheritance that is namespaced for the api

class Api::V1::SomeController < ApplicationController

So when I am creating a controller for the view that I want, I need to set up the class a little differently and inherit direction from ActionController

class StatusPagesController < ActionController::Base

Now, I’ll create a folder in views called status_pages and drop an .erb file in there.

Finally, I’ll clean up my routes and get rid of all those unnecessary old routes and put a route to the status page in the correct namespace

Rails.application.routes.draw do
 root 'status_pages#index'
 resources :status_pages
.
.
.

easy.

Now that we have the status page view configured, I need to figure out what I’m actually putting there. I basically want it to tell me when any of the various feeds have reseeded.

I experimented with postgres, but I decided Redis is the way to go for this, so what I’ll do is create a number of keys in Redis for each feed, and when that feed gets updated, I’ll assign that timestamp to that key in redis, and when I render the page, I’ll check that particular key for the timestamp.

I’ll need to create a helpermethod in the controller so I can call it via erb in the view

app/controllers/status_pages_controller

class StatusPagesController < ActionController::Base

def get_last_sync(route)
  StatusPage.check_redis(route)
end

helper_method :get_last_sync
end

and I’ll need to define the class function in the model:

class StatusPage

require_relative './data_helper.rb'
require 'date'

  def self.check_redis(route)
    feed_time = DataHelper.get_last_time(route).to_i
    time_now = DateTime.now.strftime('%s').to_i
    difference = (time_now - feed_time)/60
    difference
  end
end

DataHelper.rb is where I put all my Redis methods, so I’ll create a method there to get the latest time from a feed.

/DataHelper.rb	

def self.get_last_time(route)
	time = JSON.parse(@@redis.get(route))
	time
end

Ok, so now I can get the last seed time from a particular route. All I need is a way to store that in redis. So let me create a method that will put that key into Redis for me.

def self.store_update_time(route, time)
	@@redis.set(route, time)
end

Now, whenever we reseed, if that particular feed is stored, I’ll store the updated time.
before:

require 'date'
time_now = DateTime.now.strftime('%s').to_i

.
.
.

begin
  feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
rescue Protobuf::InvalidWireType
  errors = errors + ' skipped: 123456S (wireType)'
end

and after…

begin
  feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
  DataHelper.update_time('data123456S', time_now) // pass feed and time
rescue Protobuf::InvalidWireType
  errors = errors + ' skipped: 123456S (wireType)'
end

The seeding helper file is a little complicated, so I’ll spare you the minutiae, but what’s happening here is that I download a data feed from the MTA api, and it needs to be decoded with protobuf, and something that happens quite often is the data is somehow corrupted and throws a wireType error.
So, if the decoding is successful, and we have data, I will tell DataHelper.rb that we’ve reseeded successfully.

you can learn more about my adventures with wireType errors here.

Now that all this is set up, I can go ahead and actually create the view. It’s super basic at this point, but that’s what I’m going for. I need to continue to experiment and learn as I go.

It’s not pretty, but it gets the job done- much like the rest of HustleTime.

Remember…
You can get HustleTime on both Android and iPhone free!

HustleTime Development: Release 2.0

HustleTime has seen some major improvements in the latest release. I am going to walk you through the newest feature: the ignore tab, so you can see it in action and it’s intended use case.

Let’s say you’re in the West Village and you need an uptown F train to Queens. Any of the other orange trains (the ) won’t help you.

HustleTime, however, is only showing the next two trains on the track. Let’s highlight that station to bring it to the top.

Because we don’t care about the trains shown, let’s open up the menu to get to the ignore tab.

The default view is the favorite places, but by clicking the ignore symbol we can see all the train lines in the city. Something I’d still like to figure out is how to handle the lines. Currently the MTA groups the Franklin Avenue and Rockaway with the A/C/E lines, and the Grand Central-Times Square with the 1/2/3 line, so that’s how I’m laying them out here.

Now we can click on the lines we’d like to ignore. They’ll fade in opacity to indicate they’ve been ignored.

Selecting or de-selecting ignored trains will automatically update the arrival card (you can kinda see it behind the modal in the image above. But when we close it, we now see the desired trains.

We also notice that there’s a warning in the top left that lets the user know that she’s currently ignoring some trains.

In addition to the ignore feature, this update includes a number of other changes:

Major Changes:

  • adds a menu button replacing the old open modal button
  • adds tabs for favorite places and ignored trains
  • allows users to select trains to ignore – these trains will not appear in arrival notifications

Minor Features:

  • displays a warning if you are ignoring any train lines

Minor fixes:

  • improved actual search radius to more closely match displayed radius
  • added a few missing subway stations
  • removed font scaling from arrival notifications for legibility

Thanks for using HustleTime. Android update soon to come!

Download HustleTime on android or iPhone free!

Technological Debt, Mistakes… and Apple

Apple is a bad dude. He’s a bully at times. Sure he can be nice at times, even sweet. When things are good with Apple, computers can seem amazing, fast, clean, and safe. But all that comes with a hefty pricetag. Apple needs you to update constantly, both hardware and software, and this can be challenging to lots of people out there.

I work on a hackintosh, so I have to be very careful about updating my OS – it could cause breaking changes to my system. And after reading a little about updating to Mojave to the most current version, I decided to forego updating to the last possible moment.

Unfortunately, I couldn’t keep xCode up to date, and I had updated my iPhone, so I couldn’t actually install my updates to HustleTime on my device.

I had to try to install the newest version of Mojave, and of course, I was met with intense resistance. It turned out I was using an older version of the Clover Bootloader, and had some older kexts that were causing display issues in the new update.

This issue took the better part of Friday and Sunday to fix. But with the help of some very good folks at /r/hackintosh, I was able to get up and running.

Now for the Debt Part:

I made the mistake of trying to code all of my ReactNative work for both Ios and Android out of the same directory. Being the noob that I am, I made some changes to the node modules in the process of trying to get Android up and running and I ended up breaking the Ios implementation.

For android, the problem was really that I couldn’t get the emulator to run on my mac. I ended up creating an entirely new project on a windows machine and building it there from scratch (with lots of cuts and pastes).

As for the Ios version, I made some temporary fixes, and was able to get it running on a rudimentary level, but something was broken deep inside HustleTime and I didn’t know what. It manifested like this: I could run in debug mode, but I couldn’t actually build the project in release mode. This is obviously a big problem, and in order to move forward with developing I needed to be able to test the project in release.

I searched high and wide for fixes, but nothing worked. I ended up giving up on fixing, and decided to rebuild the project in a completely different repo.

There are a number of problems with this approach: first off- I can’t just rebuild projects every time I make a breaking change. I need a way to fix things. But this seems to be a little bit of a problem for react-native. It’s losing steam in the community and there are fewer and fewer up to date resources for help. Lots of code is old and it’s hard to find solutions.

In any case, after rebuilding the project, I can’t get an ad to appear in the new version of HustleTime – I keep getting an error stating that the GoogleMobileAds Module can’t be found. It’s tremendously frustrating to have to spend so much additional time on a piece I had solved for previously.

HustleTime will be cleaner and faster and fresher than ever, I just need to iron out a few of the new kinks…

HustleTime Development: Ignoring specific train lines (part 3)

I’m excited to be rolling out a new version of HustleTime to the app store. It’s going to include some small Ui fixes, as well as a functional Ignore train feature.

Here’s your normal view: where we see some and trains rolling into High Street.

I’ll then go into the modal by clicking the Chevrons and we now have 2 tabs, a favorites as well as an ignore, I’ll click the ignore Icon and move to the ignore menu:

I’ll click on the train I want to ignore (in this case, the )

When we close the modal, the app will refresh; displaying arrivals that exclude the train, as well as display an icon indicating that you’re actively ignoring a train (this will display when ignoring any number of trains).

What this looks like from a programming perspective is such:

On the front end, App state includes an object that has a boolean for each train line.
When I send an api call, I’m including any of those values that are marked true.

let ignoredTrains = []    
let allTrains = Object.entries(this.state.shown)
allTrains.forEach( entry => entry[1] == false ? ignoredTrains.push(entry[0]) : null )

I’m transforming the object into an array that includes the keys for the objects where the boolean value is false, then including that array in the api call.

The back end receives this information and filters out all the departure which have ['train'] values that match the ignored trains. For reference, each departure is stored as its own object, looking something like the following:

{\"train\":\"1\",\"time\":1557778941,\"station\":\"138S\",\"direction\":\"S\"}

or

{ train: '1', time: 1557778941, station: '138S', direction: 'S' }

when parsed. The filter looks something like this (for northbound departures):

DataHelper.retrieve_data.find_all{|sta| sta['station'] == station.code + 'N' && ignored.include?(sta['train']) == false }

Anyway, I’m super excited to be submitting this new feature to the app store for release (hopefully) next week! Then on to Android implementation, and then I’ll see what’s next.