Jun 14, 2013

Checkvist mobile: notes on WIP

Some more notes along the way.

More on browser caches

Refreshing cached pages and templates is a PITA. But I've found a great Google Chrome's DevTools "Disable cache" option.

Also, when using remote debugging of Chrome on Android, one have to use Android's settings to reset browser caches, "reset cache" command from DevTools didn't work for me.

Routing

Adding a login page inevitably lead to the need to navigate through the app, so I need to add routing support.

I've tried can.route from CanJS - but it doesn't support HTML5 history API yet. So I'm going to use this simple routing library instead.

Given that I have a single-page app, with different URLs which actually point to the same index.html file, I have to patch NGinx configuration to redirect all pages to /index.html internally. To do this, I used information from this post.

Login

For actual login, I had to add CORS support to the Checkvist site. After that, AJAX requests to Checkvist API work nicely.

Internationalization

At some point, I understood that I want to have I18N in the app. So I went with a very simple custom solution: create a separate JSON file with mapping between messages and message codes + simple utility function which does the actual mapping in EJS templates. No need for external library :)

Testing

Time to add tests. Because it is a right thing, and right now I want to make sure that such thing as token expiration is handled correctly.

So, I added QUnit support for Javascript testing, JQuery.mockajax for mocking AJAX requests, added service messages extension for TeamCity continuous integration.

Automated testing task in the Rakefile, to run tests for every test_*.html file under "tests" directory:

    task :test do
      Dir.foreach("test") do |name|
        if name =~ /test_.*.html$/
          system("cd test && phantomjs run.qunit.js ./#{name}")
        end
      end
    end

Feel on a safe side.

AJAX loading of view templates

To allow AJAX loading of local files (in tests), start Chrome on Mac with this command:

open -a "Google Chrome" --args --allow-file-access-from-files

Loading lists from the server

Using CanJS can.Model to implement lists model for the Checkvist. The model is cached locally, into IndexedDB and updated from the server. To interact with IndexedDB so far I use DB.js + IndexedDBShim to emulate IndexedDB over WebSQL when native support is not available.

May 17, 2013

More work on Checkvist mobile infrastructure

This is the next post regarding Checkvist mobile project.

Javascript

Trying to figure out some framework requirements. What I need is:

  1. Single JS file from multiple JS files (modules)
  2. Offline app loading
  3. Client-side undoable persistent deferrable commands

For the first point I'm going to use RequireJS (and its optimizer r.js). Also, it looks like later it is cool to use almond to optimize load times.

After some jumps, managed to make requireJS to work with CanJS MVC and Zepto jQuery replacement (and even r.js optimizer works!).

For offline apps loading, offline HTML manifest works just fine. There is a trick, though - to be able to load a non-mentioned resource in the manifest file (in development mode), one have to add

NETWORK:
*

in addition to usual resources section.

Another note: if there is any problem while loading resources from the manifest file, all other files of the app are not re-loaded.
This is quite critical - you have to make sure that all resource files are present.

The third point of the list I defer so far, until real interaction with the server is implemented.

CSS

It is cool to have a single JS file, but I'd like to have the similar for the CSS.

For this, Sass looks rather good, given that it optimizes @imports + gives tons of syntax sugar for CSS.

Adding Sass wasn't difficult:

  1. Make sure ruby is installed
  2. Create Gemfile with

    source 'https://rubygems.org'
    gem "sass"
    gem 'rb-fsevent', '~> 0.9.1'
    gem 'rake'
    
  3. gem install bundler && bundle install
  4. Create Rakefile:

    require 'sass/plugin'
    namespace :sass do
      task :env do
        Sass::Plugin.options[:template_location] = 'www/scss'
        Sass::Plugin.options[:css_location] = 'www/css'
      end
    
      desc 'Updates stylesheets if necessary from their Sass templates.'
      task :update_and_compress => :env do
        `rm www/css/*`
        Sass::Plugin.options[:style] = :compressed
        Sass::Plugin.update_stylesheets
      end
    
      desc 'Watch stylesheets'
      task :watch => :env do
        Sass::Plugin.watch
      end
    end
    
    namespace :requireJs do
      desc 'Run optimizer/uglifier for JS code'
      task :optimize do
        `node r.js -o app.build.js`
      end
    end
    
    namespace :cl do
      task :build => ["sass:update_and_compress", "requireJs:optimize"] do
        print "Done!
      end
    end

For automatic conversion of scss files to css peers during the development, I have to keep the following command running:

rake sass:watch

By the way, Sass mixins are rather useful, given that there are tons existing examples, for instance this Retina support mixin.

HTML offline issues

After having continuous trouble with updating HTML5 offline manifest file and double page refresh after each change I decided to go a way when the manifest is added to the page only while building the production version of the site. I've added a very simple task to Rakefile:

task :update_for_offline do
    index = "build/www/index.html"
    updated = IO.read(index).sub("<html>", "<html manifest='checkvist.manifest'>")
    IO.write(index, updated)
end

So far so good. MVC is almost in place, JS and CSS are united and minified in the project. Time for the real meat.

May 9, 2013

Day one, persistent storage and native HTML5 wrapper research

I'm going to keep a log of useful notes on a way to mobile Checkvist app. Not sure how patient I will be, let's see.

Persistent storage

First, I tried to figure out the current options for offline storage in the mobile app.

Looks like the most obvious choice could be WebSQL, but this spec is deprecated since 2011, though supported on all major mobile platforms.

The alternative standard, IndexedDB, is much less wide-spread, and less performant.

Here is a useful overview of offline options, and here are slides from a related talk (with a big list of intermediate libraries for offline storage).

So far, I'm considering using this Ydn-DB library to create an intermediate layer for data persisting.

Native wrapper for HTML5 app

Tried PhoneGap, which is essentially Apache Cordova

This library allows to create a native app, which wraps a Web-browser and which provides a bunch of APIs for accessing native features of the device.

The first thing which stroke me was that I had to use symbolic links to share common part of the app between iOS and Android versions of the same application.

The code of the application is deployed with the native app, but you can make AJAX calls to remote servers (though I didn't test it yet).

By the way, Android plugin for IntelliJ IDEA works with Android Cordova app just fine :)

Checkvist mobile app, take 2

In short, the idea is to create a new mobile app for Checkvist with the following key capabilities:
  • offline read/write operations
  • undo for commands
  • Android/iOS compatibility
  • rich touch interface
  • using device camera to post photos as attachments in Checkvist
  • work with external keyboards
  • app upgrade without need for app approval in iOS store
I'd like to achieve this using pure HTML5/JS/CSS + native app wrapper (mostly for marketing via Apple store/Google Play categories). But anyway, it should be possible to reach the app using http://m.checkvist.com.
The task is big and ambitious, but it looks a great way to learn something new :)

Jan 30, 2013

Hiding sidebar in Checkvist with CSS3

Just wanted to share how I've used CSS3 transformations/transitions to hide sidebar in Checkvist.

Nice effect, and easily implemented - by adding a CSS class to the sidebar element (and hiding it afterwards):

div.hideSidebarProgress {

  -webkit-transform: scale(0.01);
  -webkit-transform-origin: 99% 0%;
  -moz-transform: scale(0.01);
  -moz-transform-origin: 99% 0%;
  -o-transform: scale(0.01);
  -o-transform-origin: 99% 0%;
  -ms-transform: scale(0.01);
  -ms-transform-origin: 99% 0%;
  transform: scale(0.01);
  transform-origin: 99% 0%;

  margin-top: -80px;
  -webkit-transition-property: margin-top, transform;
  -webkit-transition-duration: 1s;
  -webkit-transition-timing-function: ease;

  transition-property: margin-top, transform;
  transition-duration: 1s;
  transition-timing-function: ease;
}

Here I use CSS3 transform to scale the sidebar down, and CSS3 transition for margin-top and for the transform scaling to create the effect.

The benefit - user immediately knows where to look for the "show sidebar" option, when needed.