Friday, 16 March 2012

MetroYam–Windows Phone Edition

Part 3 of a series on developing .NET clients for Yammer.
For the Windows Phone version of MetroYam, I'm again making use of RestSharp to handle the REST API work. Once difference when using RestSharp on Windows Phone compared to the desktop is that it only exposes asynchronous methods.
So instead of calling Execute, we have to call the ExecuteAsync method. Here's the equivalent code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Gardiner.MetroYam.Core;
using RestSharp;
using RestSharp.Authenticators;

namespace Gardiner.MetroYam.PhoneClient
{
    public class Network
    {
        private readonly ObservableCollection<MessageViewModel> _messages;

        public Network( ObservableCollection<MessageViewModel> messages )
        {
            _messages = messages;
        }

        public void Request()
        {
            var client = new RestClient();

            client.BaseUrl = "https://www.yammer.com/";
            string consumerKey = "12312341234243";
            string consumerSecret = "kciud84kf0943kdi4kdk3kr";

            var oauth_token = "84944kf984444";
            var oauth_token_secret = "kdiekemfckcfiejekmekduj454j4";

            var request = new RestRequest( "api/v1/messages.json", Method.GET );
            client.Authenticator = OAuth1Authenticator.ForProtectedResource(
                consumerKey, consumerSecret, oauth_token, oauth_token_secret
                );

            // 2011/03/28 20:39:12 +0000
            request.DateFormat = "yyyy/MM/dd HH:mm:ss zzzzz";

            client.ExecuteAsync<MessageList>( request, Callback );
        }

        private void Callback( RestResponse<MessageList> response, RestRequestAsyncHandle arg2 )
        {
            var users = new Dictionary<int, User>();
            foreach ( Reference reference in response.Data.References )
            {
                if ( reference.Type == "user" )
                    users.Add( reference.Id, new User { Fullname = reference.FullName, Photo = reference.mugshot_url } );
            }

            foreach ( var message in response.Data.messages )
            {
                var vm = new MessageViewModel();
                vm.Body = message.Body.Plain;
                vm.Created = message.created_at.LocalDateTime;
                User user = users[ message.sender_id ];
                vm.Sender = user.Fullname;
                if ( user.Photo != null )
                    vm.PhotoUrl = user.Photo;
                else
                    vm.PhotoUrl = new Uri( "https://c64.assets-yammer.com/images/no_photo_small.gif" );

                _messages.Add( vm );
            }
        }
    }
}
Rather than a single method that returns the list of messages, we now have a class that populates an ObservableCollection. A reference to this is passed in via the constructor. The ViewModel page that calls this code is straight forward:
using System;
using System.Collections.ObjectModel;
using Caliburn.Micro;
using Gardiner.MetroYam.Core;

namespace Gardiner.MetroYam.PhoneClient
{
    public class MainPageViewModel : Screen
    {
        private ObservableCollection<MessageViewModel> _messages;
        public ObservableCollection<MessageViewModel> Messages
        {
            get { return _messages; }
            set { _messages = value;
                NotifyOfPropertyChange( () => Messages );
            }
        }

        public MainPageViewModel()
        {
            Messages = new ObservableCollection<MessageViewModel>();
        }

        protected override void OnInitialize()
        {
            base.OnInitialize();

            var network = new Network( Messages );

            network.Request();
        }
    }
}
Through the magic of Caliburn Micro, the public properties exposed by this ViewModel class are automatically bound to the View. In this case we are using a simple Panorama-based Windows Phone application template. The result is an application that looks like this:
Screenshot of MetroYap on Windows Phone
It would be nice leverage the Reactive Extensions (Rx) library to simplify access to the ExecuteAsync method, but because this method doesn't just return an IAsyncResult this is a little more involved. Sergy posted to the RestSharp group an example last year, but I assume the RestSharp and Rx libraries have changed since then as his code no longer compiles for me.

No comments: