Thursday, September 11, 2014

Facebook user authentication

On the client side, you can call the facebook api to login a user using their popup window. The user will get an very temporary (few hours) access-token which will be saved in a cookie (by the facebook api automatically) and can be used to create API calls.

In this post, we will discuss user-authentication which not quite the same. We want that our server will know that the user is legit.

How it worked before:

Signup: You had the signup with user&password pair (and usually an email for lost password...). The server will save this pair in the database.
Sign-in: Each time you attemp to login, you send the pair to the server and the server replay with good or bad.
Sign-in cookie: As you don`t want the user to re-login in every page, or every day, you can decide to put a cookie which bypass this. An (unscure) cookie can be the user&password, but in this case, if someone browse the cookies on the user-machine, he can see them. It can also be a temporary access-token, for example a unique number which is good for one-day and saved with the user/password pair. Tomorrow, it will be deleted and the user will have to relogin.

Facebook

Signup: The user already signed-up for facebook years ago. The user will have to permit your application to see his details, on the first login.
Sign-in: The user will see facebook login.
Sign-in cookie: facebook, behind-the-scenes, saves a temporary access-token so you will not have to, so if you call the user login again after a short time, the user will not see the login dialog.

The user code for using it is quite short, as facebook sdk is doing most of the work, and can be seen at facebook api login samples.


Combine the two: But how can my server know that the user is a legit one?

On the client side, if login was correct, we can send the server the facebook user-id and use it as a token.  This is only half-secure, as anyone with some programming skills, can get the facebook user id and modify our client code to impersonate someone else.
But, it will stop non-programmers/hackers , and can be used for prototyping ONLY stage.

A proper authentication is to send the server the facebook-id and the access-token (which is like user/password pair). The server will ask facebook to verify it (instead of looking into the local database) and then send a temporary cookie, which is the same as the user/password cookie. This will used so the user will not have to login again and again, and that our server will not have to verify the facebook-id/access-token.


And to the implementation

  1. Do it yourself means server code for asking facebook verification and a db table with facebook-user-id to session.
  2. On node.js you can use passport library to do part of the work for you.
  3. Using facebook parse clould-server framework, they will do all of it, including storing the db for you (but it is limiting as you won't have direct access)











Tuesday, September 9, 2014

PhoneGap build

Phonegap build is a great tool for quick testing of your mobile-app as a deployed app. Copy all the relevant client file locally  (including /js /img /css folders). Make sure you have index.html file. create a simple config.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"  xmlns:gap="http://phonegap.com/ns/1.0" id="org.me.myname"  versionCode="10" version="1.1.0">
  <!-- versionCode is optional and Android only -->
  <name>MyName</name>
  <description>
    MyName application
  </description>
  <author href="http:/mynameme.org" email="myname@gmail.com">
    Developer name
  </author>
</widget>


Problems 

Facebook login depends on a domain which you configure at facebook. If your local app is deployed on the phone and the page is not served from the server, it will fail to work.

TODO: solution: load your facebook-login page from the server.

beauty:  to have integration with facebook native app, you will need to work harder and use a plugin, lets ignore for now.


JQueryMobile performance

JQueryMobile defaults are extraordinarily bad and causing bad performance. But with few tweaks you can get to an almost-native performance, as long as you are ok with reduced visual beautiy.



See this blog for some ideas/details/hacks.

Bad transitions performance on Android

On Android, on page-change there is a bad effect of "jitter" once before moving to the page. It is not seen of chrome pc.
few approaches (see stackoverflow)
1 .Simplest : change transition to none.  Fastest on Android. data-transition="none". It also make the wierd temporary scroll bar go away
$.mobile.defaultPageTransition  = 'none';
$.mobile.defaultDialogTransition = 'none';
$.mobile.buttonMarkup.hoverDelay = 0;
Note some places say u need user-scalalbe=no on phono gap even with none data-transition


2. using this: <meta name="viewport" content="width=device-width, user-scalable=no" /> fade works fine without flicker, but it is still slow.


Slider/Range widgets are very slow on Android

known issue planned to be solved on 1.5
iOS performance was ok, on the one sample iPhone I tried.


Click delay?

http://stackoverflow.com/questions/10028782/replace-all-click-events-with-tap-in-jquery-mobile-to-
speed-up


1. One common internet opinition is to use https://github.com/ftlabs/fastclick . Didn`t help me much... 2.   On JQueryMobile I believe there is another issue. When you click on a button, the blue highlight starts, and the actual action/page-transition occurs considrable time afterwards (half-a-second...) You can remove this, by adding the fastclick class to relevant elements, and declare it as below, but note that it can cause problems with paenl buttons and that the highlight disappears...
$( document ).one( "pageshow", function() {
       $(".fastclick").on("vclick",function(e){
               $(this).trigger("click");
               e.preventDefault();
               return false;
           }); 
});

TODO; an actual better solution will be to first trigger an async-action (like page change), then show the blue-highlight. It appears it work the other way around now.
wierdly checkbox widget work better.


How to choose multi-panel or single?:

Approach 1 -  copy the header,panel each time.

It is better to have two separate pages (.html), one which each page in it.

<div data-role="page" id="profile">

    <div data-role="header" data-position="fixed" data-tap-toggle="false" data-id="persist1" data-theme="a">
      <span class="ui-title">Edit Profile</span>
        <div class="ui-btn-right">
            <a id="myButton1" class="headerButton" href="index.html" data-role="button">profile</a>            <a id="myButton2" class="headerButton" href="index2.html" data-role="button">index2</a>

        </div>
        <a href="#myPanel" class="ui-btn ui-btn-left ui-icon-bars ui-btn-icon-notext">not-visible</a>

    </div>
   <div data-role="panel" id="myPanel">
        <h2>Panel Header</h2>
        <p>panel2.</p>
     </div>
      <div data-role="main" class="ui-content" id="main-profile">
         profile page
      </div>

</div> <!--of page-->

in this case, there are three issues:
1. Ugly code which is the same for each page.
2. On transition, the header slightly change, unless you add it data-theme='a' (the default is bad)
3. If you are using multiple pages in the same .html file, after you move to another page, the panel fails to work, probably as we have multiple panels with the same-id.


Approach 2 -  external header and panel

In this case, you have to add a script above ,otherwise the external panel/header will not shown at all.

<script>
  $( document ).on( "pagecreate", function() {
  $( "body > [data-role='panel']" ).panel();
  $( "body > [data-role='panel'] [data-role='listview']" ).listview();
  });

  $( document ).one( "pageshow", function() {
   $( "body > [data-role='header']" ).toolbar();
   $( "body > [data-role='header'] [data-role='navbar']" ).navbar();
  });

 </script>

</head>

<body>

<div data-role="header" data-id="my-header" data-position="fixed" data-tap-toggle="false" data-theme="b">
    <h1>First</h1>
     <a href="#myPanel" class="ui-btn ui-btn-left ui-icon-bars ui-btn-icon-notext">not-visible</a>
    <div class="ui-btn-right">
            <a id="myButton1" class="headerButton" href="#first" data-prefetch="true" data-transition="fade" data-role="button">profile</a>
            <a id="myButton2" class="headerButton" href="#second" data-prefetch="true" data-transition="fade" data-role="button">home</a>
        </div>
</div><!-- /header -->

<div data-role="panel" id="myPanel">
        <h2>Panel Header</h2>
        <p>You can close the panel by clicking outside the panel, pressing the Esc key or by swiping.</p>
</div>

<div data-role="page" id="first">
    <div data-role="content"> 
        <p>The content</p>     
        <p>View internal page called <a href="#second">second</a></p> 
    </div><!-- /content -->
</div>

<div data-role="page" id="second">
    <div data-role="content"> 
        <p>I'm the second content 2nd</p>     
        <p><a href="#first">Back to first</a></p> 
    </div><!-- /content -->
</div>


</body>


Issues with this approach
1. You will have to change pragmatically the title, active-button of the header once you move to another page
2. still need the data-theme="a" to have any theme to your page.


Let`s discuss the events in this scenario.  If you programtically modify the content of the page and not loading a different page, the beforecreate,create and init event will be called the first time you move to that page, but not afterwards.  Also remember you have to manually call the page enhance, if your change require widget change.
(Navigate from A to B)
page B---pagebeforecreate   (ONCE)
page B---pagecreate            (ONCE)
page B---pageinit                 (ONCE)
page A---pagebeforehide
page B---pagebeforeshow
page A---pageremove
page A---pagehide
page B---pageshow 

            activepage =  $.mobile.pageContainer.pagecontainer("getActivePage");

            console.log( activepage[0].id);
You can put it on the pagebeforeshow event , as example.




Panels

For Android -like drawer:
 <div data-role="panel" id="myPanel"  data-display="overlay" data-position-fixed="true" data-theme="a">
data-position-fixed="true"  - you content will always jump up when you open.
data-display="overlay"  for Android style, use "push"/"reveal" for iOS style.







Monday, September 8, 2014

Profile editor using facebook data

Let`s create a simple mobile web-app, which let you to login into facebook and choose few images which you like best.

UX:


At facebook you you can query albums list and then the images of each album.
We will have 3 screens, using jquerymobile for the UI

  1. Login screen, where standard facebook login will happen. There will also be one big button "Find image"
  2. List of album names and number of images in each. each row is clickable. We use simple  jquerymobile list , see w3schools demo
  3. A grid of images of the current album. see here



Code:

Facebook login and API calls

First login using facebook, and then make API calls:
graph api explorer can be used to simulate requests.
[TODO] see the full documentation to handle batching/errors

Fetch albums

We will calls FB.api('/me/albums',fucntion) , get the result JSON and extract the albums from it. We will later replace the html of the content div and call JQueryMobile enhanceWithin() to reformat it, otherwise the list will use the browser default list.
Please note that by doing so, we ignore the url hashing scheme, so "back" will not work automatically for us. We should change this later...

function fetchAlbums()
  {
    console.log('fetchAlbums...');
    FB.api('/me/albums', function(result) {
      var html=  '<ul data-role="listview" data-inset="true">';
      html+='<h2>Albums</h2>'
      for (var i=0; i<result.data.length; i++)
      {
        var album = result.data[i];
        if (typeof album.count == "undefined")
          album.count=0;
        html+='<li><a href="#" onclick="fetchImages('+album.id+')">'+album.name+'<span class="ui-li-count">'+album.count +'</span></a></li>';
      }
      html+='</ul>';
      $( "#main" ).html(html).enhanceWithin();

    });
  }

[syntax-highlighting taken from here using brush: javascript like so: <pre class="brush: javascript">]

TODO: an album has a cover_photo property which can be added as a small icon.

Fetch album images

FB.API(albumId+"/photos") will show a list of photos.
Each photo contains array of images and a small 100px picture. doc here
Some, rather ugly, js code is needed to transform gridSize=3 to ui-grid-b(33% each) and ui-block-a/b/c. 

function fetchImages(albumId)
  {
     var request = albumId+'/photos';
     console.log('calling'+ request);
     FB.api(albumId+'/photos',function(result) {
       
       var gridSize=3; //2 or more
       var maxGridChar= String.fromCharCode("a".charCodeAt(0)+gridSize-2);
       //solo/a/b/c -b means 33%*3
       var html='<div class="ui-grid-' + maxGridChar   +'">';
       
       for (var i=0; i<result.data.length;i++)     {
         var uiBlockIndex= String.fromCharCode("a".charCodeAt(0) +i%gridSize);
         html+='<div class="ui-block-'+uiBlockIndex+'">';
         html+=  '<img src="'+result.data[i].picture+'"/>';
         html+="</div>";
       }
       html+='</div>';
       $("#main").html(html).enhanceWithin();
     });
  }

This table is clearly non responsive and looks rather bad. You can add some css to make it looks a bit better here:

.ui-grid-c img {

  width:100%;

  height:auto;

  margin:10px;

}

.ui-grid-c div {

  padding:10px;

}

JQueryMobile proper dynamic pages

This was a quick-and-dirty sample, where we just replaced the html of the ui-content and called enhancedWithin, by doing so we ignored the built-in pages system and also ignored the links routing (we used onclick to replace the content instead of using the href.
TODO: use pagecontainer widget for a proper way to do it. make sure panels still work well in this case.