In part 6 of this NGTweet related series, we saw how to use a value converter to convert date time value into a relative time using IValueConverter. All this while we have been dealing with the timeline and mentions functionality. Being a Twitter client, the minimal requirement of sending the tweet was missing from the application until now. So in this post I decided to add the tweet functionality to NGTweet.
Refactor existing bits
I think it will be easier to add related functionalities like Retweet and reply or what is also known as the direct message in Twitter terms. I can reuse some bits of tweet functionality to build those features. Before starting with tweet, I just looked at existing codebase and found some duplication in the Timeline and Mentions functionalities.
I did some refactoring by extracting the common functionality into a TimeLineViewModelBase class. The common collections, the busy indicator and the references to the injected dependencies were moved to the base. This code clean up allowed me to have the functionality specific to the timeline and mentions features to be available in those view models. in its current state only the service calls, their callbacks and the DispatcherTimer for periodic updates resides in these view models. Another minor change I did was to fetch 100 messages for the first time when the user is logged in in the timeline section. By default the TweetSharp library fetched only 20 tweets.
Build Send Tweet functionality
Lets look at what we are trying to achieve at the end of this post.
Compared to previous post, the rightmost column of Compose has been added. This is used to send a new tweet to all the follower. The UI is very simple. It has a single textbox and a button to publish the tweet. The whole thing is built as a user control called TweetAction. The functionality of this control is almost same as the first example that I built using MVVM. I won’t go into the depth of it as its very basic in nature. It has some validation built into it to make sure that the send button is enabled only when there is some valid text to send. The button is disabled until the text is entered in the textbox.
On the server side I added a method which will use the TweetSharp library to send the tweet. This follows the usual Request & Response approach used in other parts of the application.
[OperationContract]
SendTweetResponse SendTweet(SendTweetRequest request);
The request needs to have the authentication token and the text that needs to set as the status.
[DataContract]
public class SendTweetRequest
{
[DataMember]
public string Status { get; set; }
[DataMember]
public OAuthAccessToken AccessToken { get; set; }
}
Once the status has been updated successfully, we get back a TweeterStatus as a response from the TweetSharp library. Like we did on previous occasions we wrap it up in NGTweeterStatus and return it back to the client in the response object.
[DataContract]
public class SendTweetResponse
{
[DataMember]
public NGTweeterStatus TweeterStatus { get; set; }
}
The service implementation is straight forward which does the marshalling of different objects involved in this operation
public SendTweetResponse SendTweet(SendTweetRequest request)
{
_twitterService.AuthenticateWith(request.AccessToken.Token, request.AccessToken.TokenSecret);
TwitterStatus twitterStatus = _twitterService.SendTweet(request.Status);
TweeterStatusAdapter tweeterStatusAdapter = new TweeterStatusAdapter();
SendTweetResponse response = new SendTweetResponse { TweeterStatus = tweeterStatusAdapter.Convert(twitterStatus) };
return response;
}
If everything goes fine, the tweet will be updated to the Twitter as the status update. We need to update the client with the updated status. This is handled in the service callback method which add the returned tweet to the existing timeline.
internal void OnTweetActionCommand()
{
IsBusy = true;
SendTweetRequest sendTweetRequest = new SendTweetRequest
{
AccessToken = (OAuthAccessToken)_applicationSettingsProvider["accessToken"],
Status = TweetText
};
_authenticationService.BeginSendTweet(sendTweetRequest, SendTweetCallback, null);
}
internal void SendTweetCallback(IAsyncResult ar)
{
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
IsBusy = false;
SendTweetResponse sendTweetResponse = _authenticationService.EndSendTweet(ar);
TweetText = string.Empty;
Messenger.Default.Send(sendTweetResponse.TweeterStatus, "SendTweetSuccess");
});
}
Decoupled communication between view models using MVVM Light
There are couple of things to note in the above code. First is that the DispatcherHelper is used to update the collection as well as other properties. Since these properties are data bound to the UI controls, the updates to these properties needs to happen on the UI thread. Otherwise we get cross thread access exception as I had mentioned in the previous post.
The second point to note is the usage of Messenger class. This class is available in MVVMLight framework. In this case we are trying to update the Timeline from the Send methods view model. For this feature, we need to pass the status which is returned from the service call to the TimeLine view model. There are different ways of passing data between view models. If we are not using any framework like MVVMLight or Prism then we can raise an event with the updated data as part of event argument. We can subscribe to the event in the other view model where we need to update the UI. But this approach introduces some coupling between the two view models.
Frameworks like Prism and MVVMLight provide ways of decoupling this logic using some sort of universal event bus. In Prism its called EventAggregator and MVVMLight offers the Messenger class. We can can publish and subscribe events in a totally decoupled fashion using one of these offerings. MVVM Light has a default implementation for Messenger functionality. It also offers an interface IMessenger which can be used if the default implementation is not sufficient for our needs.
In our case I am using an overload of the Send of Messenger method which uses the data that needs to be published along with a token. The token can be any type of object. This is used for sending an event to a targeted subscriber. Anyone who subscribes for the event using the same token will receive the notification. This bit takes care of the publishing part.
Messenger.Default.Send(sendTweetResponse.TweeterStatus, "SendTweetSuccess");
On the subscriber side we use the following syntax
Messenger.Default.Register<NGTweeterStatus>(this, "SendTweetSuccess", AddNewTweet);
We use the Register method to register the event subscription. We use the overload of this method which uses the token and the last parameter is the name of the method within this class which will be called when the event is triggered. As we can see, the TweetActionViewModel does not know anything about it’s consumer while publishing the event using the send method. Also the TimeLineViewModel does not know anything about the publisher, when it subscribes using the Register method. In future we can change the implementation of both the timeline as well as tweet action view models without impacting one another.
Conclusion
In this post I added the send tweet functionality to the application. This functionality will be extended to retweet and direct message in future. While adding this feature we saw how MVVM Light can be used to decouple the communication between view models using the Messenger class. Decoupling the publisher and subscriber helps us in building extensible code which is easier to maintain. The moment we couple the two view models together, future changes become difficult to implement.
As always the complete source code is available for download.
Until next time Happy Programming
No comments:
Post a Comment