public void DisplayInfoForPost(Post post) { // Sets how the cell appears based on the Post passed in ActivityIndicator.StartAnimating (); if(post.ImageRecord != null) ImageViewInCell.Image = post.ImageRecord.FullImage; fontName = (string)(NSString)post.PostRecord[Post.FontKey]; TextLabelInCell.Text = (string)(NSString)post.PostRecord[Post.TextKey]; }
public async void LoadNewPostsWithRecordID(CKRecordID recordID) { // Called when AppDelegate receives a push notification // The post that triggered the push may not be indexed yet, so a fetch on predicate might not see it. // We can still fetch by recordID though CKDatabase publicDB = CKContainer.DefaultContainer.PublicCloudDatabase; try { CKRecord record = await publicDB.FetchRecordAsync (recordID); Post postThatFiredPush = new Post(record); postThatFiredPush.LoadImage(null, TableView.ReloadData); postManager.LoadNewPosts(postThatFiredPush); } catch(NSErrorException ex) { Console.WriteLine (ex.Error); } }
public void Submit(Post post) { postManager.LoadNewPosts (post); }
int PostComparison(Post first, Post second) { var firstDate = (DateTime)first.PostRecord.CreationDate; var secondDate = (DateTime)second.PostRecord.CreationDate; return firstDate.CompareTo (secondDate); }
public void ResetWithTagString(string tags) { // Reloads table with new tag settings // First, anything the table is updating with now is potentially invalid, cancel any current updates fetchRecordQueue.CancelAllOperations (); // This should only be filled with array add operations, best to just wait for it to finish updateCellArrayQueue.DispatchSync (() => { }); // Resets the table to be empty PostCells.Clear (); lastPostSeenOnServer = null; reloadHandler (); // Sets tag array and prepares table for initial update tagArray = string.IsNullOrWhiteSpace (tags) ? new string[0] : tags.ToLower ().Split (new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries); postCursor = null; isLoadingBatch = false; haveOldestPost = false; LoadBatch (); }
void OnLoadNewPostComplted(CKQueryCursor cursor, NSError operationError, List<Post> newPosts, Post lastRecordInOperation, Post retryPost) { Error error = HandleError (operationError); switch (error) { case Error.Success: // lastRecordCreationDate is the most recent record we've seen on server, let's set our property to that for next time we get a push lastPostSeenOnServer = lastRecordInOperation ?? lastPostSeenOnServer; // This sorts the newPosts array in ascending order newPosts.Sort (PostComparison); // Takes our newPosts array and inserts the items into the table array one at a time foreach (Post p in newPosts) { updateCellArrayQueue.DispatchAsync (() => { PostCells.Insert (0, p); DispatchQueue.MainQueue.DispatchAsync (reloadHandler); }); } DispatchQueue.MainQueue.DispatchAsync (RefreshControl.EndRefreshing); break; case Error.Retry: Utils.Retry(()=> LoadNewPosts(retryPost), operationError); break; case Error.Ignore: Console.WriteLine ("Error: {0}", operationError.Description); DispatchQueue.MainQueue.DispatchAsync(RefreshControl.EndRefreshing); break; default: throw new NotImplementedException (); } }
void OnLoadNewPostFetchRecord(CKRecord record, List<Post> newPosts, ref Post lastRecordInOperation) { // If the record we just fetched doesn't match recordIDs to any item in our newPosts array, let's make an Post and add it var matchingRecord = newPosts.FindIndex (p => p.PostRecord.Id.Equals(record.Id)); if (matchingRecord == -1) { Post fetchedPost = new Post (record); newPosts.Add (fetchedPost); fetchedPost.LoadImage(new string[] { Image.FullsizeKey }, reloadHandler); lastRecordInOperation = fetchedPost; } else { // If we already have this record we don't have to fetch. We'll still update lastRecordInOperation because we did see it on the server lastRecordInOperation = newPosts[matchingRecord]; } }
bool MatchesTags(Post post) { if (post == null) return false; var weakTags = post.PostRecord [Post.TagsKey]; string[] postTags = NSArray.StringArrayFromHandle (weakTags.Handle); // All tags from seach field (tagArray) must be presented inside post foreach (string tag in tagArray) { if (!postTags.Contains (tag)) return false; } // if we are here => post contains all tags from search field return true; }
// Called when users pulls to refresh // This adds new items to the beginning of the table public void LoadNewPosts(Post post = null) { // If we don't have any posts on our table yet, // fetch the first batch instead (we make assumptions in this method that we have other posts to compare to) if (TryLoadFirstBatch ()) return; // We want to strip all posts we have that haven't been seen on the server yet from tableview (order isn't guaranteed) var loc = PostCells.IndexOf (lastPostSeenOnServer); List<Post> newPosts = PostCells.Take (loc).ToList (); PostCells.RemoveRange (0, loc); // If we had a post passed in and it matches our tags, we should put that in the array too if (MatchesTags (post)) newPosts.Add (post); else post = null; // The last record we see will be the most recent we see on the server, we'll set the property to this in the completion handler Post lastRecordInOperation = null; var postQuery = CreateLoadNewPostQuery (); var queryOp = new CKQueryOperation (postQuery); queryOp.DesiredKeys = desiredKeys; queryOp.RecordFetched = (CKRecord record) => OnLoadNewPostFetchRecord (record, newPosts, ref lastRecordInOperation); queryOp.Completed = (CKQueryCursor cursor, NSError operationError) => OnLoadNewPostComplted (cursor, operationError, newPosts, lastPostSeenOnServer, post); queryOp.Database = PublicDB; fetchRecordQueue.AddOperation (queryOp); }
void OnPostLoadingCompleted(CKQueryCursor cursor, NSError operationError) { Error error = HandleError (operationError); switch (error) { case Error.Success: postCursor = cursor; haveOldestPost = cursor == null; isLoadingBatch = false; PostCells.AddRange (downloadingBatchStorage); if (lastPostSeenOnServer == null && PostCells.Count > 0) { lastPostSeenOnServer = PostCells [0]; DispatchQueue.MainQueue.DispatchAsync(RefreshControl.EndRefreshing); } DispatchQueue.MainQueue.DispatchAsync (reloadHandler); break; case Error.Retry: Utils.Retry (() => { isLoadingBatch = false; LoadBatch (); }, operationError); break; case Error.Ignore: isLoadingBatch = false; DispatchQueue.MainQueue.DispatchAsync (RefreshControl.EndRefreshing); Console.WriteLine ("Error: {0}", operationError.Description); break; default: break; } }
void OnPostFetched(CKRecord record) { // When we get a record, use it to create an Post Post fetchedPost = new Post (record); downloadingBatchStorage.Add (fetchedPost); fetchedPost.LoadImage (new string[]{ Image.FullsizeKey }, () => { // Once image is loaded, tell the tableview to reload DispatchQueue.MainQueue.DispatchAsync (reloadHandler); }); }
void PublishPost (NSObject sender) { // Prevents multiple posting, locks as soon as a post is made PostButton.Enabled = false; UIActivityIndicatorView indicator = new UIActivityIndicatorView (UIActivityIndicatorViewStyle.Gray); indicator.StartAnimating (); PostButton.CustomView = indicator; // Hides the keyboards and dispatches a UI update to show the upload progress HiddenText.EndEditing (true); TagField.EndEditing (true); ProgressBar.Hidden = false; // Creates post record type and initizalizes all of its values CKRecord newRecord = new CKRecord (Post.RecordType); newRecord [Post.FontKey] = (NSString)ImageLabel.Font.Name; newRecord [Post.ImageRefKey] = new CKReference (ImageRecord.Record.Id, CKReferenceAction.DeleteSelf); newRecord [Post.TextKey] = (NSString)HiddenText.Text; string[] tags = TagField.Text.ToLower ().Split (new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries); newRecord [Post.TagsKey] = NSArray.FromObjects (tags); Post newPost = new Post (newRecord); newPost.ImageRecord = ImageRecord; // Only upload image record if it is not on server, otherwise just upload the new post record CKRecord[] recordsToSave = ImageRecord.IsOnServer ? new CKRecord[] { newRecord } : new CKRecord[] { newRecord, ImageRecord.Record }; // TODO: https://trello.com/c/A9T8Spyp second param is null CKModifyRecordsOperation saveOp = new CKModifyRecordsOperation (recordsToSave, new CKRecordID[0]); saveOp.PerRecordProgress = (CKRecord record, double progress) => { // Image record type is probably going to take the longest to upload. Reflect it's progress in the progress bar if (record.RecordType == Image.RecordType) InvokeOnMainThread (() => { var val = (float)(progress * 0.95); ProgressBar.SetProgress (val, true); }); }; // When completed it notifies the tableView to add the post we just uploaded, displays error if it didn't work saveOp.Completed = (CKRecord[] savedRecords, CKRecordID[] deletedRecordIDs, NSError operationError) => { Error errorResponse = HandleError (operationError); switch (errorResponse) { case Error.Success: // Tells delegate to update so it can display our new post InvokeOnMainThread (() => { DismissViewController (true, null); MainController.Submit (newPost); }); break; case Error.Retry: CKErrorInfo errorInfo = new CKErrorInfo (operationError.UserInfo); nint retryAfter = errorInfo.RetryAfter.HasValue ? errorInfo.RetryAfter.Value : 3; Console.WriteLine ("Error: {0}. Recoverable, retry after {1} seconds", operationError.Description, retryAfter); Task.Delay ((int)retryAfter * 1000).ContinueWith (_ => PublishPost (sender)); break; case Error.Ignore: Console.WriteLine ("Error saving record: {0}", operationError.Description); string errorTitle = "Error"; string dismissButton = "Okay"; string errorMessage = operationError.Code == (long)CKErrorCode.NotAuthenticated ? "You must be logged in to iCloud in order to post" : "Unrecoverable error with the upload, check console logs"; InvokeOnMainThread (() => { UIAlertController alert = UIAlertController.Create (errorTitle, errorMessage, UIAlertControllerStyle.Alert); alert.AddAction (UIAlertAction.Create (dismissButton, UIAlertActionStyle.Cancel, null)); PostButton.Enabled = true; PresentViewController (alert, true, null); ProgressBar.Hidden = true; PostButton.CustomView = null; }); break; default: throw new NotImplementedException (); } }; CKContainer.DefaultContainer.PublicCloudDatabase.AddOperation (saveOp); }