#pragma warning restore /// <summary> /// Gets a video url from redgif /// </summary> /// <param name="url"></param> /// <returns></returns> private async Task <string> GetRedGifUrlFromWatchUrl(string url) { // Return if we have nothing. if (url.Equals(string.Empty)) { return(string.Empty); } try { var lastSegment = new Uri(url).Segments.Last(); // Make the call var result = await RedGifHelper.GetVideoInfoAsync(lastSegment); return(result?.DataInfo?.VideoInfo == null ? url : result.DataInfo.VideoInfo.StandardDefUrl); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("failed to get image from redgif", e); TelemetryManager.ReportUnexpectedEvent(this, "FailedRedGifApiCall", e); } return(string.Empty); }
/// <summary> /// Fired when we should load the content. /// </summary> public void OnPrepareContent() { // Since this can be costly kick it off to a background thread so we don't do work // as we are animating. Task.Run(async() => { // Get the video Uri var youTubeVideoInfo = await GetYouTubeVideoInfoAsync(_contentPanelBase.Source); var youtubeUrl = GetYouTubeUrl(youTubeVideoInfo); // Back to the UI thread with pri await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { if (string.IsNullOrWhiteSpace(youtubeUrl)) { // If we failed fallback to the browser. _contentPanelBase.FireOnFallbackToBrowser(); TelemetryManager.ReportUnexpectedEvent(this, "FailedToGetYoutubeVideoAfterSuccess"); return; } // Setup the video _videoPlayer = new MediaElement { AutoPlay = false, AreTransportControlsEnabled = true }; _videoPlayer.CurrentStateChanged += VideoPlayerOnCurrentStateChanged; _videoPlayer.Source = new Uri(youtubeUrl); ui_contentRoot.Children.Add(_videoPlayer); }); }); }
/// <summary> /// Fired when we should load the content. /// </summary> public void OnPrepareContent() { // Run our work on a background thread. Task.Run(() => { // Get the image Url var imageUrl = ImageManager.GetImageUrl(_baseContentPanel.Source.Url); // Make sure we got it. if (string.IsNullOrWhiteSpace(imageUrl)) { // This is bad, we should be able to get the url. TelemetryManager.ReportUnexpectedEvent(this, "BasicImageControlNoImageUrl"); // Jump back to the UI thread _baseContentPanel.FireOnFallbackToBrowser(); return; } // Make sure we aren't destroyed. if (_baseContentPanel.IsDestroyed) { return; } // Fire off a request for the image. var request = new ImageManager.ImageManagerRequest { ImageId = _baseContentPanel.Source.Id, Url = imageUrl }; request.OnRequestComplete += OnRequestComplete; App.BaconMan.ImageMan.QueueImageRequest(request); }); }
/// <summary> /// Called when the user is trying to comment on something. /// </summary> /// <returns>Returns the json returned or a null string if failed.</returns> public static async Task <string> SendRedditComment(BaconManager baconMan, string redditIdCommentingOn, string comment, bool isEdit = false) { string returnString = null; try { // Build the data to send var postData = new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("thing_id", redditIdCommentingOn), new KeyValuePair <string, string>("text", comment) }; var apiString = isEdit ? "api/editusertext" : "api/comment"; // Make the call returnString = await baconMan.NetworkMan.MakeRedditPostRequestAsString(apiString, postData); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to send comment", e); baconMan.MessageMan.DebugDia("failed to send message", e); } return(returnString); }
/// <summary> /// Creates a new webview, this should be called under lock! /// </summary> private void MakeWebView() { if (_webView != null) { return; } // Make the web-view _webView = new WebView(WebViewExecutionMode.SeparateThread); // Setup the listeners, we need all of these because some web pages don't trigger // some of them. _webView.FrameNavigationCompleted += NavigationCompleted; _webView.NavigationFailed += NavigationFailed; _webView.DOMContentLoaded += DomContentLoaded; _webView.ContentLoading += ContentLoading; _webView.ContainsFullScreenElementChanged += ContainsFullScreenElementChanged; // Navigate try { _webView.Navigate(new Uri(_contentPanelBase.Source.Url, UriKind.Absolute)); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent(this, "FailedToMakeUriInWebControl", e); _contentPanelBase.FireOnError(true, "This web page failed to load"); } // Now add an event for navigating. _webView.NavigationStarting += NavigationStarting; // Insert this before the full screen button. ui_contentRoot.Children.Insert(0, _webView); }
/// <summary> /// Attempts to delete a post. /// </summary> /// <param name="baconMan"></param> /// <param name="postId"></param> /// <returns></returns> public static async Task <bool> DeletePost(BaconManager baconMan, string postId) { try { // Build the data to send var postData = new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("id", "t3_" + postId) }; // Make the call var returnString = await baconMan.NetworkMan.MakeRedditPostRequestAsString("api/del", postData); if (returnString.Equals("{}")) { return(true); } } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to delete post", e); baconMan.MessageMan.DebugDia("failed to delete post", e); } return(false); }
/// <summary> /// Called when we should update the inbox and send any notifications. /// </summary> /// <param name="newNotifications"></param> /// <param name="currentMessages"></param> public async Task UpdateInboxMessages(List <Tuple <string, string, string> > newNotifications, List <Message> currentMessages) { // Make sure we are enabled and in a good state if (ShowInboxOnBand && await EnsureBandTileState()) { try { var pairedBand = await GetPairedBand(); if (pairedBand == null) { // We don't have a band. return; } // Try to connect to the band. using (var bandClient = await BandClientManager.Instance.ConnectAsync(pairedBand)) { foreach (var newNote in newNotifications) { var title = newNote.Item1; var body = newNote.Item2; // If the body is empty move the title to the body so it wraps if (string.IsNullOrWhiteSpace(body)) { body = title; title = ""; } // If we have a title clip it to only two words. The title can't be very long and // looks odd if it is clipped on the band. var firstSpace = string.IsNullOrWhiteSpace(title) ? -1 : title.IndexOf(' '); if (firstSpace != -1) { if (title != null) { var secondSpace = title.IndexOf(' ', firstSpace + 1); if (secondSpace != -1) { title = title.Substring(0, secondSpace); } } } // Send the message. await bandClient.NotificationManager.SendMessageAsync(_bandTileGuid, title, body, DateTimeOffset.Now, Microsoft.Band.Notifications.MessageFlags.ShowDialog); } } } catch (Exception e) { _baconMan.MessageMan.DebugDia("failed to update band message", e); TelemetryManager.ReportUnexpectedEvent(this, "FailedToUpdateBandMessages", e); } } }
/// <summary> /// Updates the subreddit list /// </summary> /// <param name="newSubreddits"></param> private void UpdateSubredditList(List <Subreddit> newSubreddits) { try { var insertCount = 0; for (var newListCount = 0; newListCount < newSubreddits.Count; newListCount++) { var newSubreddit = newSubreddits[newListCount]; // Set some UI properties. newSubreddit.FavIconUri = newSubreddit.IsFavorite ? "ms-appx:///Assets/MainPage/FavoriteIcon.png" : "ms-appx:///Assets/MainPage/NotFavoriteIcon.png"; newSubreddit.DisplayName = newSubreddit.DisplayName.ToLower(); // If the two are the same, just update them. if (_subreddits.Count > insertCount && _subreddits[insertCount].Id.Equals(newSubreddit.Id)) { // If they are the same just update it _subreddits[insertCount].DisplayName = newSubreddit.DisplayName; _subreddits[insertCount].FavIconUri = newSubreddit.FavIconUri; _subreddits[insertCount].IsFavorite = newSubreddit.IsFavorite; _subreddits[insertCount].Title = newSubreddit.Title; } // (subreddit insert) If the next element in the new list is the same as the current element in the old list, insert. else if (_subreddits.Count > insertCount && newSubreddits.Count > newListCount + 1 && newSubreddits[newListCount + 1].Id.Equals(_subreddits[insertCount].Id)) { _subreddits.Insert(insertCount, newSubreddit); } // (subreddit remove) If the current element in the new list is the same as the next element in the old list. else if (_subreddits.Count > insertCount + 1 && newSubreddits.Count > newListCount && newSubreddits[newListCount].Id.Equals(_subreddits[insertCount + 1].Id)) { _subreddits.RemoveAt(insertCount); } // If the old list is still larger than the new list, replace else if (_subreddits.Count > insertCount) { _subreddits[insertCount] = newSubreddit; } // Or just add. else { _subreddits.Add(newSubreddit); } insertCount++; } // Remove any extra subreddits while (_subreddits.Count > newSubreddits.Count) { _subreddits.RemoveAt(_subreddits.Count - 1); } } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent(this, "UpdateSubredditListFailed", e); App.BaconMan.MessageMan.DebugDia("UpdateSubredditListFailed", e); } }
/// <summary> /// Fired when we should load the content. /// </summary> public async void OnPrepareContent() { // Defer so we give the UI time to work. await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { var headerText = ""; var minorText = ""; if (_contentPanelBase.Source.IsSelf) { headerText = "There's no content here!"; minorText = "Scroll down to view the discussion."; } else { _content = MiscellaneousHelper.TryToFindRedditContentInLink(_contentPanelBase.Source.Url); switch (_content.Type) { case RedditContentType.Subreddit: headerText = "This post links to a subreddit"; minorText = $"Tap anywhere to view /r/{_content.Subreddit}"; break; case RedditContentType.Comment: headerText = "This post links to a comment thread"; minorText = "Tap anywhere to view it"; break; case RedditContentType.Post: headerText = "This post links to a reddit post"; minorText = $"Tap anywhere to view it"; break; case RedditContentType.User: headerText = "This post links to a reddit user page"; minorText = $"Tap anywhere to view {_content.User}"; break; case RedditContentType.Website: // This shouldn't happen App.BaconMan.MessageMan.DebugDia("Got website back when prepare on reddit content control"); TelemetryManager.ReportUnexpectedEvent(this, "GotWebsiteOnPrepareRedditContent"); break; default: throw new ArgumentOutOfRangeException(); } } ui_headerText.Text = headerText; ui_minorText.Text = minorText; // Hide loading _contentPanelBase.FireOnLoading(false); }); }
/// <summary> /// Hide the loading text when the markdown is done. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MarkdownBox_OnMarkdownReady(object sender, MarkdownReadyArgs e) { if (e.WasError) { _contentPanelBase.FireOnFallbackToBrowser(); TelemetryManager.ReportUnexpectedEvent(this, "FailedToShowMarkdown", e.Exception); } else { // Hide loading _contentPanelBase.FireOnLoading(false); } }
/// <summary> /// Saves, unsaves, hides, or unhides a reddit item. /// </summary> /// <returns>Returns null if it fails or the user doesn't exist.</returns> public static async Task <bool> SaveOrHideRedditItem(BaconManager baconMan, string redditId, bool?save, bool?hide) { if (!baconMan.UserMan.IsUserSignedIn) { baconMan.MessageMan.ShowSigninMessage(save.HasValue ? "save item" : "hide item"); return(false); } var wasSuccess = false; try { // Make the data var data = new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("id", redditId) }; string url; if (save.HasValue) { url = save.Value ? "/api/save" : "/api/unsave"; } else if (hide.HasValue) { url = hide.Value ? "/api/hide" : "/api/unhide"; } else { return(false); } // Make the call var jsonResponse = await baconMan.NetworkMan.MakeRedditPostRequestAsString(url, data); if (jsonResponse.Contains("{}")) { wasSuccess = true; } else { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to save or hide item, unknown response"); baconMan.MessageMan.DebugDia("failed to save or hide item, unknown response"); } } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to save or hide item", e); baconMan.MessageMan.DebugDia("failed to save or hide item", e); } return(wasSuccess); }
/// <summary> /// Assuming there are images, this does the rotation of the lock screen images. /// </summary> /// <returns></returns> private async Task DoImageRotation(UpdateTypes type) { try { var wasSuccess = false; switch (type) { case UpdateTypes.LockScreen: wasSuccess = await DoSingleImageRotation(UpdateTypes.LockScreen); break; case UpdateTypes.Desktop: wasSuccess = await DoSingleImageRotation(UpdateTypes.Desktop); break; case UpdateTypes.Band: wasSuccess = await DoSingleImageRotation(UpdateTypes.Band); break; case UpdateTypes.All: break; default: { var firstSuccess = await DoSingleImageRotation(UpdateTypes.LockScreen); var secondSuccess = await DoSingleImageRotation(UpdateTypes.Desktop); var thirdSuccess = await DoSingleImageRotation(UpdateTypes.Band); wasSuccess = firstSuccess && secondSuccess && thirdSuccess; break; } } // If we successfully updated set the time. if (wasSuccess) { LastImageUpdate = DateTime.Now; } } catch (Exception e) { _baconMan.MessageMan.DebugDia("Failed to set background image", e); TelemetryManager.ReportUnexpectedEvent(this, "Failed to set background image", e); } }
/// <summary> /// Fires FireOnDestroyContnet on the UI thread. /// </summary> /// <param name="host"></param> /// <param name="panel"></param> private async Task FireOnDestroyContent(IContentPanelBase panelBase) { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { try { panelBase.OnDestroyContent(); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("FireOnRemovePanel failed", e); TelemetryManager.ReportUnexpectedEvent(this, "FireOnRemovePanelFailed", e); } }); }
/// <summary> /// Fires OnPanelUnloaded on the UI thread. /// </summary> /// <param name="host"></param> /// <param name="panel"></param> private async void FireOnPanelUnloaded(IContentPanelHost host) { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { try { // Then tell the pane has been unloaded. host.OnPanelUnloaded(); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("FireOnPanelUnloaded failed", e); TelemetryManager.ReportUnexpectedEvent(this, "FireOnPanelUnloadedFailed", e); } }); }
/// <summary> /// Called by the consumer when a massage should be changed /// </summary> public void ChangeMessageReadStatus(Message message, bool isRead, int messagePosition = 0) { // Using the post and suggested index, find the real post and index var collectionMessage = message; FindMessageInCurrentCollection(ref collectionMessage, ref messagePosition); if (collectionMessage == null || messagePosition == -1) { // We didn't find it. return; } // Update the status collectionMessage.IsNew = isRead; // Fire off that a update happened. FireCollectionUpdated(messagePosition, new List <Message> { collectionMessage }, false, false); // Start a task to make the vote Task.Run(async() => { try { // Build the data var request = collectionMessage.IsNew ? "/api/unread_message" : "/api/read_message"; var postData = new List <KeyValuePair <string, string> >(); postData.Add(new KeyValuePair <string, string>("id", collectionMessage.GetFullName())); // Make the call var str = await _mBaconMan.NetworkMan.MakeRedditPostRequestAsString(request, postData); // Do some super simple validation if (str != "{}") { throw new Exception("Failed to set message status! The response indicated a failure"); } } catch (Exception ex) { _mBaconMan.MessageMan.DebugDia("failed to set message status!", ex); TelemetryManager.ReportUnexpectedEvent(this, "failedToSetMessageRead", ex); } }); }
/// <summary> /// Callback when we get the image. /// </summary> /// <param name="sender"></param> /// <param name="response"></param> private async void OnRequestComplete(object sender, ImageManager.ImageManagerResponseEventArgs response) { // Remove the event var request = (ImageManager.ImageManagerRequest)sender; request.OnRequestComplete -= OnRequestComplete; // Jump back to the UI thread await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { if (!response.Success) { TelemetryManager.ReportUnexpectedEvent(this, "BasicImageControlNoImageUrl"); _baseContentPanel.FireOnFallbackToBrowser(); return; } lock (this) { if (_baseContentPanel.IsDestroyed) { // Get out of here if we should be destroyed. return; } // Grab the source, we need this to make the image _imageSourceStream = response.ImageStream; // Add the image to the UI _image = new Image(); // We don't want to wait on this. #pragma warning disable CS4014 // Set the image. ReloadImage(false); #pragma warning restore CS4014 // Set the image into the UI. ui_scrollViewer.Content = _image; // Setup the save image tap _image.RightTapped += ContentRoot_RightTapped; _image.Holding += ContentRoot_Holding; } }); }
public static Type GetControlType(ContentPanelSource source, object callingClass = null) { try { if (GifImageContentPanel.CanHandlePost(source)) { return(typeof(GifImageContentPanel)); } if (RedditVideoContentPanel.CanHandlePost(source)) { return(typeof(RedditVideoContentPanel)); } if (YoutubeContentPanel.CanHandlePost(source)) { return(typeof(YoutubeContentPanel)); } if (BasicImageContentPanel.CanHandlePost(source)) { return(typeof(BasicImageContentPanel)); } if (MarkdownContentPanel.CanHandlePost(source)) { return(typeof(MarkdownContentPanel)); } if (RedditContentPanel.CanHandlePost(source)) { return(typeof(RedditContentPanel)); } if (CommentSpoilerContentPanel.CanHandlePost(source)) { return(typeof(CommentSpoilerContentPanel)); } if (WindowsAppContentPanel.CanHandlePost(source)) { return(typeof(WindowsAppContentPanel)); } } catch (Exception e) { // If we fail here we will fall back to the web browser. App.BaconMan.MessageMan.DebugDia("Failed to query can handle post", e); TelemetryManager.ReportUnexpectedEvent(callingClass, "FailedToQueryCanHandlePost", e); } return(typeof(WebPageContentPanel)); }
/// <summary> /// Fires OnContentPreloading on the UI thread. /// </summary> /// <param name="host"></param> /// <param name="panel"></param> private async void FireOnContentPreloading(IContentPanelHost host) { // Do this on a high pri so the loading indicator will show up ASAP. await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () => { try { // Then tell the content has begun loading. host.OnContentPreloading(); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("FireOnContentPreloading failed", e); TelemetryManager.ReportUnexpectedEvent(this, "FireOnContentPreloadingFailed", e); } }); }
#pragma warning restore /// <summary> /// Uses GfyCat to convert a normal .gif into a video /// </summary> /// <param name="gifUrl"></param> /// <returns></returns> private async Task <string> ConvertGifUsingGfycat(string gifUrl) { // Return if we have nothing. if (gifUrl.Equals(string.Empty)) { return(string.Empty); } try { var url = $"https://upload.gfycat.com/transcode?fetchUrl={gifUrl}"; // Make the call var webResult = await NetworkManager.MakeGetRequest("https://upload.gfycat.com/transcode?fetchUrl=" + gifUrl); // Get the input stream and json reader. // NOTE!! We are really careful not to use a string here so we don't have to allocate a huge string. var inputStream = await webResult.ReadAsInputStreamAsync(); using (var reader = new StreamReader(inputStream.AsStreamForRead())) using (JsonReader jsonReader = new JsonTextReader(reader)) { // Parse the Json as an object var serializer = new JsonSerializer(); var gfyData = await Task.Run(() => serializer.Deserialize <GfyCatConversionData>(jsonReader)); // Validate the response var mp4Url = gfyData.Mp4Url; if (string.IsNullOrWhiteSpace(mp4Url)) { throw new Exception("Gfycat failed to convert"); } // Return the url return(mp4Url); } } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("failed to convert gif via gfycat", e); TelemetryManager.ReportUnexpectedEvent(this, "GfyCatConvertFailed", e); } return(string.Empty); }
/// <summary> /// Gets a video url from gfycat /// </summary> /// <param name="apiUrl"></param> /// <returns></returns> private async Task <string> GetGfyCatGifUrl(string apiUrl) { // Return if we have nothing. if (apiUrl.Equals(string.Empty)) { return(string.Empty); } try { // Make the call var webResult = await NetworkManager.MakeGetRequest(apiUrl); // Get the input stream and json reader. // NOTE!! We are really careful not to use a string here so we don't have to allocate a huge string. var inputStream = await webResult.ReadAsInputStreamAsync(); using (var reader = new StreamReader(inputStream.AsStreamForRead())) using (JsonReader jsonReader = new JsonTextReader(reader)) { // Parse the Json as an object var serializer = new JsonSerializer(); var gfyData = await Task.Run(() => serializer.Deserialize <GfyCatDataContainer>(jsonReader)); // Validate the response var mp4Url = gfyData.Item.Mp4Url; if (string.IsNullOrWhiteSpace(mp4Url)) { throw new Exception("Gfycat response failed to parse"); } // Return the url return(mp4Url); } } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("failed to get image from gfycat", e); TelemetryManager.ReportUnexpectedEvent(this, "FaileGfyCatApiCall", e); } return(string.Empty); }
/// <summary> /// Gets a reddit user. /// </summary> /// <returns>Returns null if it fails or the user doesn't exist.</returns> public static async Task <User> GetRedditUser(BaconManager baconMan, string userName) { User foundUser = null; try { // Make the call var jsonResponse = await baconMan.NetworkMan.MakeRedditGetRequestAsString($"user/{userName}/about/.json"); // Parse the new user foundUser = await ParseOutRedditDataElement <User>(baconMan, jsonResponse); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to search for user", e); baconMan.MessageMan.DebugDia("failed to search for user", e); } return(foundUser); }
/// <summary> /// Fires FireOnPanelStolen on the UI thread. /// </summary> /// <param name="host"></param> /// <param name="panel"></param> private async Task FireOnRemovePanel(IContentPanelHost host, IContentPanelBase panelBase) { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { try { // Tell the panel the host is gone panelBase.OnHostRemoved(); // And remove the panel. host.OnRemovePanel(panelBase); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("FireOnRemovePanel failed", e); TelemetryManager.ReportUnexpectedEvent(this, "FireOnRemovePanelFailed", e); } }); }
/// <summary> /// Fires OnPanelAvailable on the UI thread. /// </summary> /// <param name="host"></param> /// <param name="panel"></param> private async void FireOnPanelAvailable(IContentPanelHost host, IContentPanelBase panelBase) { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { try { // First tell the post it has a host panelBase.OnHostAdded(host); // Then tell the host it has a panel. host.OnPanelAvailable(panelBase); } catch (Exception e) { App.BaconMan.MessageMan.DebugDia("FireOnPanelAvailable failed", e); TelemetryManager.ReportUnexpectedEvent(this, "FireOnPanelAvailableFailed", e); } }); }
private void ReadingMode_Tapped(object sender, TappedRoutedEventArgs e) { // Show loading try { lock (_lockObject) { ui_readingModeLoading.Visibility = Visibility.Visible; ui_readingModeLoading.IsActive = true; ui_readingModeIconHolder.Visibility = Visibility.Collapsed; _webView.Navigate(new Uri($"http://www.readability.com/m?url={_contentPanelBase.Source.Url}", UriKind.Absolute)); } } catch (Exception ex) { TelemetryManager.ReportUnexpectedEvent(this, "FailedToNavReadingMode", ex); } TelemetryManager.ReportEvent(this, "ReadingModeEnabled"); }
private void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { if (_mShareComment != null) { var commentLink = "https://reddit.com" + _post.Permalink + _mShareComment.Id; // #todo use a markdown-less body text var shareBody = _mShareComment.Body.Length > 50 ? _mShareComment.Body.Substring(0, 50) + "..." : _mShareComment.Body; args.Request.Data.Properties.ApplicationName = "Baconit"; args.Request.Data.Properties.ContentSourceWebLink = new Uri(commentLink, UriKind.Absolute); args.Request.Data.Properties.Title = "A Reddit Post Shared From Baconit"; args.Request.Data.Properties.Description = shareBody; args.Request.Data.SetText($" \r\n\r\n{shareBody}\r\n\r\n{commentLink}"); _mShareComment = null; TelemetryManager.ReportEvent(this, "CommentShared"); } else { args.Request.FailWithDisplayText("Baconit doesn't have anything to share!"); TelemetryManager.ReportUnexpectedEvent(this, "FailedToShareCommentHelperCommentNoShareComment"); } }
/// <summary> /// Called when the user added a comment or edit an existing comment. /// </summary> /// <returns></returns> public bool CommentAddedOrEdited(string parentOrOrgionalId, string serverResponse, bool isEdit) { // Assume if we can find author we are successful. Not sure if that is safe or not... :) if (!string.IsNullOrWhiteSpace(serverResponse) && serverResponse.Contains("\"author\"")) { // Do the next part in a try catch so if we fail we will still report success since the // message was sent to reddit. try { // Parse the new comment var newComment = MiscellaneousHelper.ParseOutRedditDataElement <Comment>(_baconMan, serverResponse) .Result; if (isEdit) { UpdateComment(newComment); } else { // Inject the new comment InjectComment(parentOrOrgionalId, newComment); } } catch (Exception e) { // We f****d up adding the comment to the UI. _baconMan.MessageMan.DebugDia("Failed injecting comment", e); TelemetryManager.ReportUnexpectedEvent(this, "AddCommentSuccessButAddUiFailed"); } // If we get to adding to the UI return true because reddit has the comment. return(true); } // Reddit returned something wrong _baconMan.MessageMan.ShowMessageSimple("That's not right", "Sorry we can't post your comment right now, reddit returned and unexpected message."); TelemetryManager.ReportUnexpectedEvent(this, "CommentPostReturnedUnexpectedMessage"); return(false); }
/// <summary> /// Update the posts in flip view. Staring at the index given and going until the list is empty. /// </summary> /// <param name="startingPos"></param> /// <param name="newPosts"></param> private async void UpdatePosts(int startingPos, IReadOnlyList <Post> newPosts) { await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => { var flipViewMenuVis = _host.CurrentScreenMode() == ScreenMode.Single ? Visibility.Visible : Visibility.Collapsed; // Grab the list lock lock (_postsLists) { // If we are currently in a deferred scenario then we need to handle updates // differently since the list won't match the list that is expected if (_deferredPostList.Count != 0) { if (_postsLists.Count <= 0 || newPosts.Count <= 0 || !_postsLists[0].Context.Post.Id.Equals(newPosts[0].Id)) { return; } // The current post is updated, so update it. // We can't replace the time because flip view will freak out // so just update whatever UI we need to update. _postsLists[0].Context.Post.Likes = newPosts[0].Likes; _postsLists[0].Context.Post.SubTextLine1 = newPosts[0].SubTextLine1; _postsLists[0].Context.Post.SubTextLine2PartOne = newPosts[0].SubTextLine2PartOne; _postsLists[0].Context.Post.SubTextLine2PartTwo = newPosts[0].SubTextLine2PartTwo; _postsLists[0].Context.Post.Domain = newPosts[0].Domain; _postsLists[0].Context.Post.Score = newPosts[0].Score; // We have done all we want to do, leave now. return; } // If the list is currently empty we want to only load the first element and defer the rest of the // elements. If the target post is -1 we load the first element, if not we load it only. var deferLoadPosts = _postsLists.Count == 0; var deferTargetPost = _targetPost; _targetPost = null; if (deferLoadPosts) { // If we are doing a defer make sure we have a target if (string.IsNullOrWhiteSpace(deferTargetPost) && newPosts.Count > 0) { deferTargetPost = newPosts[0].Id; } } // Now setup the post update var insertIndex = startingPos; // Set up the objects for the UI foreach (var post in newPosts) { if (post == null) { continue; } // Check if we are adding or inserting. var isReplace = insertIndex < _postsLists.Count; if (isReplace) { if (_postsLists[insertIndex].Context.Post.Id.Equals(post.Id)) { // We can't replace the time because flip view will freak out // so just update whatever UI we need to update. _postsLists[insertIndex].Context.Post.Likes = post.Likes; _postsLists[insertIndex].Context.Post.SubTextLine1 = post.SubTextLine1; _postsLists[insertIndex].Context.Post.SubTextLine2PartOne = post.SubTextLine2PartOne; _postsLists[insertIndex].Context.Post.SubTextLine2PartTwo = post.SubTextLine2PartTwo; _postsLists[insertIndex].Context.Post.Domain = post.Domain; _postsLists[insertIndex].Context.Post.Score = post.Score; } else { // Replace the entire post if it brand new _postsLists[insertIndex].Context.Post = post; } } else { // If we are deferring posts only add the target if (deferLoadPosts) { if (post.Id.Equals(deferTargetPost)) { // Try catch is a work around for bug https://github.com/QuinnDamerell/Baconit/issues/53 try { _postsLists.Add(new FlipViewPostItem(_host, _collector, post, _targetComment)); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent(this, "UpdatePosts", e); App.BaconMan.MessageMan.DebugDia("Adding to postList failed! (deferLoadPosts)", e); } } // Add it to the deferred list, also add the deferred post so we know // where it is in the list. _deferredPostList.Add(post); } else { // Otherwise, just add it. // Try catch is a work around for bug https://github.com/QuinnDamerell/Baconit/issues/53 try { _postsLists.Add(new FlipViewPostItem(_host, _collector, post, _targetComment)); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent(this, "UpdatePosts", e); App.BaconMan.MessageMan.DebugDia("Adding to postList failed! (!deferLoadPosts)", e); } } } // Set the menu button post.FlipViewMenuButton = flipViewMenuVis; // Add one to the insert index insertIndex++; } // If the item source hasn't been set yet do it now. if (ui_flipView.ItemsSource == null) { ui_flipView.ItemsSource = _postsLists; } } // Hide the loading overlay if it is visible HideFullScreenLoading(); }); }
/// <summary> /// Fired when a user taps view context. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ViewContext_OnButtonTapped(object sender, EventArgs e) { // Get the message var message = (Message)((FrameworkElement)sender).DataContext; // We need to get all of the parts of this message we need to send to flip view // The comment replies only have the post id in the "context" so use that for both. string postId = null; try { var context = message.Context; var contextIndex = context.IndexOf('/'); var slashSeenCount = 0; while (contextIndex != -1) { slashSeenCount++; // Iterate one past the / contextIndex++; if (slashSeenCount == 4) { // After 4 slashes we should have the post id var nextSlash = context.IndexOf('/', contextIndex); postId = context.Substring(contextIndex, nextSlash - contextIndex); break; } contextIndex = context.IndexOf('/', contextIndex); } if (string.IsNullOrEmpty(postId)) { throw new Exception("post id was empty"); } } catch (Exception ex) { App.BaconMan.MessageMan.DebugDia("failed to parse message context", ex); App.BaconMan.MessageMan.ShowMessageSimple("Oops", "Something is wrong and we can't show this context right now."); TelemetryManager.ReportUnexpectedEvent(this, "failedToParseMessageContextString", ex); return; } // Navigate flip view and force it to the post and comment. var args = new Dictionary <string, object>(); args.Add(PanelManager.NavArgsSubredditName, message.Subreddit); args.Add(PanelManager.NavArgsForcePostId, postId); args.Add(PanelManager.NavArgsForceCommentId, message.Id); // Make sure the page Id is unique _mPanelHost.Navigate(typeof(FlipViewPanel), message.Subreddit + SortTypes.Hot + SortTimeTypes.Week + postId + message.Id, args); // Also if it is unread set it to read if (message.IsNew) { MarkAsRead_Tapped(sender, e); } }
/// <summary> /// Submits a new reddit post /// </summary> public static async Task <SubmitNewPostResponse> SubmitNewPost(BaconManager baconMan, string title, string urlOrText, string subredditDisplayName, bool isSelfText, bool sendRepliesToInbox) { if (!baconMan.UserMan.IsUserSignedIn) { baconMan.MessageMan.ShowSigninMessage("submit a new post"); return(new SubmitNewPostResponse { Success = false }); } try { // Make the data var data = new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("kind", isSelfText ? "self" : "link"), new KeyValuePair <string, string>("sr", subredditDisplayName), new KeyValuePair <string, string>("sendreplies", sendRepliesToInbox ? "true" : "false") }; data.Add(isSelfText ? new KeyValuePair <string, string>("text", urlOrText) : new KeyValuePair <string, string>("url", urlOrText)); data.Add(new KeyValuePair <string, string>("title", title)); // Make the call var jsonResponse = await baconMan.NetworkMan.MakeRedditPostRequestAsString("/api/submit/", data); // Try to see if we can find the word redirect and if we can find the subreddit url var responseLower = jsonResponse.ToLower(); if (responseLower.Contains("redirect") && responseLower.Contains($"://www.reddit.com/r/{subredditDisplayName}/comments/")) { // Success, try to parse out the new post link var startOfLink = responseLower.IndexOf($"://www.reddit.com/r/{subredditDisplayName}/comments/"); if (startOfLink == -1) { return(new SubmitNewPostResponse { Success = false }); } var endofLink = responseLower.IndexOf('"', startOfLink); if (endofLink == -1) { return(new SubmitNewPostResponse { Success = false }); } // Try to get the link var link = "https" + jsonResponse.Substring(startOfLink, endofLink - startOfLink); // Return return(new SubmitNewPostResponse { Success = true, NewPostLink = link }); } // We have a reddit error. Try to figure out what it is. for (var i = 0; i < Enum.GetNames(typeof(SubmitNewPostErrors)).Length; i++) { var enumName = Enum.GetName(typeof(SubmitNewPostErrors), i).ToLower();; if (!responseLower.Contains(enumName)) { continue; } TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to submit post; error: " + enumName); baconMan.MessageMan.DebugDia("failed to submit post; error: " + enumName); return(new SubmitNewPostResponse { Success = false, RedditError = (SubmitNewPostErrors)i }); } TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to submit post; unknown reddit error: "); baconMan.MessageMan.DebugDia("failed to submit post; unknown reddit error"); return(new SubmitNewPostResponse { Success = false, RedditError = SubmitNewPostErrors.Unknown }); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to submit post", e); baconMan.MessageMan.DebugDia("failed to submit post", e); return(new SubmitNewPostResponse { Success = false }); } }
/// <summary> /// Attempts to parse out a reddit object from a generic reddit response /// json containing a data object. We can't do a generic DataObject here because /// the function handles data blocks that are in various depths in the json. /// </summary> /// <param name="baconMan"></param> /// <param name="originalJson"></param> /// <returns></returns> public static async Task <T> ParseOutRedditDataElement <T>(BaconManager baconMan, string originalJson) { // TODO make this async. If I try to Task.Run(()=> the parse the task returns but the // await never resumes... idk why. try { // Try to parse out the data object var dataPos = originalJson.IndexOf("\"data\":", StringComparison.Ordinal); if (dataPos == -1) { return(default(T)); } var dataStartPos = originalJson.IndexOf('{', dataPos + 7); if (dataStartPos == -1) { return(default(T)); } // There can be nested { } in the data we want // To do this optimally, we will just look for the close manually. var dataEndPos = dataStartPos + 1; var depth = 0; var isInText = false; while (dataEndPos < originalJson.Length) { // If we find a " make sure we ignore everything. if (originalJson[dataEndPos] == '"') { if (isInText) { // If we are in a text block look if it is an escape. // If it isn't an escape, end the text block. if (originalJson[dataEndPos - 1] != '\\') { isInText = false; } } else { // We entered text. isInText = true; } } // If not in a text block, look for {} if (!isInText) { // If we find an open +1 to depth if (originalJson[dataEndPos] == '{') { depth++; } // If we find and end.. else if (originalJson[dataEndPos] == '}') { // If we have no depth we are done. if (depth == 0) { break; } // Otherwise take one off. depth--; } } dataEndPos++; } // Make sure we didn't fail. if (depth != 0) { return(default(T)); } // Move past the last } dataEndPos++; var dataBlock = originalJson.Substring(dataStartPos, (dataEndPos - dataStartPos)); return(JsonConvert.DeserializeObject <T>(dataBlock)); } catch (Exception e) { TelemetryManager.ReportUnexpectedEvent("MisHelper", "failed to parse data element", e); } return(default(T)); }