MetroYam–WPF desktop edition
Part 2 of a series on developing .NET clients for Yammer.
The first client that I've attempted is the WPF version. This version of MetroYam uses the MahApps.Metro library to enable a Metro interface.
Once the UI framework is in place it's time to register with Yammer for an API key. You need this to access their REST API. Initially this just allows you to access the Developer Yammer group, but it may be possible to apply to Yammer to make it useable by any Yammer user.
Using the OAuth1 integration test from the RestSharp source code repository as a guide, I created a class with the following method prototype accessing a feed of messages (keys changed to protect the innocent!)
public List<MessageViewModel> Request()
{
var client = new RestClient();
client.BaseUrl = "https://www.yammer.com/";
string consumerKey = "0121212121212";
string consumerSecret = "abcdefgkskdlasllkjsfien3234sdfsdf";
client.Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret );
var request = new RestRequest( "oauth/request\_token", Method.POST );
var response = client.Execute( request );
var qs = HttpUtility.ParseQueryString( response.Content );
var oauth\_token = qs\[ "oauth\_token" \];
var oauth\_token\_secret = qs\[ "oauth\_token\_secret" \];
request = new RestRequest( "oauth/authorize" );
request.AddParameter( "oauth\_token", oauth\_token );
var url = client.BuildUri( request ).ToString();
Process.Start( url );
var verifier = "01234"; // <-- Breakpoint here (set verifier in debugger)
request = new RestRequest( "oauth/access\_token", Method.POST );
client.Authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, oauth\_token, oauth\_token\_secret, verifier
);
response = client.Execute( request );
qs = HttpUtility.ParseQueryString( response.Content );
oauth\_token = qs\[ "oauth\_token" \];
oauth\_token\_secret = qs\[ "oauth\_token\_secret" \];
Debug.WriteLine("oauth\_token {0}", oauth\_token);
Debug.WriteLine( "oauth\_token\_secret {0}", oauth\_token\_secret );
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";
RestResponse<MessageList> responseList = client.Execute<MessageList>( request );
var users = new Dictionary<int, User>();
foreach ( Reference reference in responseList.Data.References )
{
if ( reference.Type == "user" )
users.Add( reference.Id, new User() { Fullname = reference.FullName, Photo = reference.mugshot\_url});
}
var messages = new List<MessageViewModel>();
foreach ( var message in responseList.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);
}
return messages;
}
The Models (Message, User etc) and ViewModel (MessageViewModel) are declared in the 'portable' class library. This library is shared between the WPF, Windows Phone and Metro-style WinRT applications.
This particular method causes a web browser to load part-way through to obtain a user authentication code. Normally an application would cache the oauth_token and oauth_token_secret values returned by the "oauth/access_token" call so the user wouldn't need to re-authenticate the app each time.
The relevant XAML to render this is pretty straightforward (not that I'm a XAML expert like Nigel!)
<ListView Grid.Column="1" Grid.Row="1" Margin="20,0,0,0" x:Name="Messages"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<Border Padding="0,0,0,10">
<DockPanel ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<Image Width="48" Height="48" Source="{Binding PhotoUrl, IsAsync=True}" DockPanel.Dock="Left" VerticalAlignment="Top" />
<TextBlock Text="{Binding Path=Sender}" DockPanel.Dock="Top" FontWeight="Bold" />
<TextBlock Text="{Binding Path=Created}" DockPanel.Dock="Top" />
<TextBlock Text="{Binding Path=Body}" DockPanel.Dock="Bottom" TextWrapping="WrapWithOverflow" />
</DockPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And to confirm that this does actually render something, here's a screenshot of work in progress:
I like how the <Image/>
element can take an Internet URL so that it just loads the photos automatically.
My plan is that the client will default to the 'company' list in the first column, but let you add additional columns to follow other groups that you are a member of.
Categories: .NET