Example #1
0
		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);
		}
Example #4
0
		int PostComparison(Post first, Post second)
		{
			var firstDate = (DateTime)first.PostRecord.CreationDate;
			var secondDate = (DateTime)second.PostRecord.CreationDate;
			return firstDate.CompareTo (secondDate);
		}
Example #5
0
		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 ();
		}
Example #6
0
		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 ();
			}
		}
Example #7
0
		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];
			}
		}
Example #8
0
		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;
		}
Example #9
0
		// 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);
		}
Example #10
0
		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;
			}
		}
Example #11
0
		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);
		}