using System; using System.Collections.Generic; using System.Device.Location; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Hammock; using MahApps.RESTBase; using MahApps.Twitter; using MahApps.Twitter.Delegates; using MahApps.Twitter.Models; using MahTweets.Extensions.Twitter.Mango.Extensions; using MahTweets.Mango.Core; using MahTweets.Mango.Core.Models; using MahTweets.Mango.Core.Repositories; using MahTweets.Mango.Core.Services; using Tomers.Phone.Controls; using WindowsPhoneEssentials.ComponentModel; using WindowsPhoneEssentials.Threading; namespace MahTweets.Extensions.Twitter.Mango { public class TwitterConnection : NotifyPropertyChanged, IMicroblogConnection { private readonly TwitterClient _client; private readonly IContactRepository _contacts; private readonly IStatusService _statusService; private readonly object _ownerLock = new object(); private volatile Contact _owner; private readonly AccountCredentials _accountCredentials; private readonly object _refreshingLock = new object(); public TwitterConnection(TwitterClient client, AccountCredentials accountCredentials, IContactRepository contacts, IStatusService statusService) { _accountCredentials = accountCredentials; _client = client; _contacts = contacts; _statusService = statusService; client.SetOAuthToken(new Credentials { OAuthToken = accountCredentials.Username, OAuthTokenSecret = accountCredentials.Password }); Refresh(); } public void Dispose() { } public bool IsRefreshing { get; private set; } public void Refresh() { if (!IsRefreshing) { lock (_refreshingLock) { if (!IsRefreshing) { var homeTimelineTask = Task.Factory.StartNew( SynchroniseLoadMessages(_client.Statuses.BeginHomeTimeline, HomeTimelineCallback)); var mentionsTask = Task.Factory.StartNew( SynchroniseLoadMessages(_client.Statuses.BeginMentions, HomeTimelineCallback)); var dmsTask = Task.Factory.StartNew( SynchroniseLoadMessages(_client.DirectMessages.BeginDirectMessages, HomeTimelineCallback)); var sendDmsTask = Task.Factory.StartNew( SynchroniseLoadMessages(_client.DirectMessages.BeginSentDirectMessages, HomeTimelineCallback)); Task.Factory.ContinueWhenAll( new[] {homeTimelineTask, mentionsTask, dmsTask, sendDmsTask}, tasks => { IsRefreshing = false; }); } } } } public void HomeTimelineCallback(RestRequest request, RestResponse response, Object obj) { try { if (obj is List) { foreach (var status in from t in (List)obj select t.ToStatus(_contacts)) { status.ToTimeline(Credentials); _statusService.OnNext(status); } } else if (obj is List) { foreach (var t in (List)obj) { var contact = _contacts.GetContact(t.Sender.ScreenName); contact.UpdateContact(t.Sender); var recipient = _contacts.GetContact(t.Recipient.ScreenName); recipient.UpdateContact(t.Recipient); var status = t.ToStatus(contact); status.Recipient = recipient; status.ToTimeline(Credentials); _statusService.OnNext(status); } } } catch (Exception ex) { //TODO better logging/error handling here Debug.WriteLine(ex); } } private static Action SynchroniseLoadMessages(Action beginGet, Action callback) { return () => { var resetEvent = new ManualResetEvent(false); beginGet((request, response, obj) => { resetEvent.Set(); callback(request, response, obj); }); resetEvent.WaitOne(30000); }; } public bool ReadOnly { get { return false; } } public void NewStatusUpdate(string text, string conversationId, GeoCoordinate location) { var outputTweets = TwitterHelper.SplitIntoMessages(text); if (outputTweets.Count > 1) { // multiple messages - format accordingly for (var i = 0; i < outputTweets.Count; i++) { if (i == 0) outputTweets[i] = string.Format("{0}…", outputTweets[i]); else if (i == outputTweets.Count - 1) { string contents; var prefix = TwitterHelper.GetTweetPrefix(outputTweets[i], out contents); outputTweets[i] = string.Format("{0}…{1}", prefix, contents); } else { string contents; var prefix = TwitterHelper.GetTweetPrefix(outputTweets[i], out contents); outputTweets[i] = string.Format("{0}…{1}…", prefix, contents); } } } if (outputTweets.Count <= 0) return; foreach (var tweet in outputTweets .Where(i => !string.IsNullOrEmpty(i)) .Select(value => location == null ? NewTweet(value, conversationId, null, null) : NewTweet(value, conversationId, location.Latitude, location.Longitude))) { HandleNewTweet(tweet, text, conversationId, location); } } public void Share(StatusUpdate status) { //SynchroniseLoadMessages(r => _client.Statuses.BeginRetweet(status.Id, r), HomeTimelineCallback); } private void HandleNewTweet(object tweet, string text, string conversationId, GeoCoordinate location) { if (tweet is Tweet) { var t = (Tweet)tweet; var status = t.ToStatus(_contacts); status.ToTimeline(Credentials); _statusService.OnNext(status); } else if (tweet is DirectMessage) { var t = (DirectMessage)tweet; var contact = _contacts.GetContact(t.Sender.ScreenName); contact.UpdateContact(t.Sender); var recipient = _contacts.GetContact(t.Recipient.ScreenName); recipient.UpdateContact(t.Recipient); var status = t.ToStatus(contact); status.Recipient = recipient; status.ToTimeline(Credentials); _statusService.OnNext(status); } else if (tweet is ExceptionResponse) { //TODO Review error handling for sending status updates from connection classes Execute.OnUIThread(() => NotificationBox.Show("Oops!", "Failed to tweet! Twitter may be down, or you may not have a connection", new NotificationBoxCommand("Retry", () => NewStatusUpdate(text, conversationId, location)), new NotificationBoxCommand("Save as draft", () => {/*TODO Save draft*/}), new NotificationBoxCommand("Discard", () => { }))); } } private object NewTweet(string tweet, string conversationId, double? lat, double? lon) { object responseObject = null; var sync = new ManualResetEvent(false); _client.Statuses.BeginUpdate(tweet, conversationId, lat, lon, (req, res, obj) => { responseObject = obj; sync.Set(); }); //TODO Maybe throw a timeout exception or something here? sync.WaitOne(10000); return responseObject; } public string OwnerName { get { return Credentials.AccountName; } } public Contact Owner { get { if (_owner == null) { lock (_ownerLock) { if (_owner == null) { var contact = _contacts.GetContact(OwnerName); if (contact == null) { contact = new Contact { Name = OwnerName, Bio = "Loading..." }; _client.Account.BeginVerifyCredentials(OwnerCallback); } _owner = contact; } } } return _owner; } set { _owner = value; OnPropertyChanged(() => Owner); } } public AccountCredentials Credentials { get { return _accountCredentials; } } public void OwnerCallback(RestRequest request, RestResponse response, object obj) { if (!(obj is User)) return; var u = (User)obj; _owner = new Contact { Name = u.ScreenName, Id = u.Id.ToString(), ImageUrl = u.ProfileImageUrl }; OnPropertyChanged(() => Owner); } } }