// This method fetches the whole ImageRecord that a user taps on and then passes it to the delegate
			public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
			{
				// If the user has already tapped on a thumbnail, prevent them from tapping any others
				if (controller.lockSelectThumbnail)
					return;
				else
					controller.lockSelectThumbnail = true;

				// Starts animating the thumbnail to indicate it is loading
				controller.ImageCollection.SetLoadingFlag (indexPath, true);

				// Uses convenience API to fetch the whole image record associated with the thumbnail that was tapped
				CKRecordID userSelectedRecordID = controller.ImageCollection.GetRecordId (indexPath);
				controller.PublicCloudDatabase.FetchRecord (userSelectedRecordID, (record, error) => {
					// If we get a partial failure, we should unwrap it
					if(error != null && error.Code == (long)CKErrorCode.PartialFailure) {
						CKErrorInfo info = new CKErrorInfo(error);
						error = info[userSelectedRecordID];
					}

					Error errorResponse = controller.HandleError(error);

					switch(errorResponse) {
						case Error.Success:
							controller.ImageCollection.SetLoadingFlag(indexPath, false);
							Image selectedImage = new Image(record);
							InvokeOnMainThread(() => {
								controller.MasterController.GoTo(controller, selectedImage);
							});
							break;

						case Error.Retry:
							Utils.Retry(()=> {
								controller.lockSelectThumbnail = false;
								ItemSelected(collectionView, indexPath);
							}, error);
							break;

						case Error.Ignore:
							Console.WriteLine ("Error: {0}", error.Description);
							string errorTitle = "Error";
							string errorMessage = "We couldn't fetch the full size thumbnail you tried to select, try again";
							string dismissButton = "Okay";
							UIAlertController alert = UIAlertController.Create(errorTitle, errorMessage, UIAlertControllerStyle.Alert);
							alert.AddAction(UIAlertAction.Create(dismissButton, UIAlertActionStyle.Cancel, null));
							InvokeOnMainThread(()=> controller.PresentViewController(alert, true, null));
							controller.ImageCollection.SetLoadingFlag(indexPath, false);
							controller.lockSelectThumbnail = false;
							break;

						default:
							throw new NotImplementedException();
					}
				});
			}
示例#2
0
		public void LoadImage (string[] keys, Action updateBlock = null)
		{
			// Fetches the imageRecord this post record references in its ImageRefKey.
			// Only fetches the values associated with the keys passed in to the NSArray
			var imgRecordId = ((CKReference)PostRecord[ImageRefKey]).RecordId;
			CKFetchRecordsOperation imageOp = new CKFetchRecordsOperation (new CKRecordID[]{
				imgRecordId
			});
			imageOp.DesiredKeys = keys;
			imageOp.Completed = (NSDictionary recordDict, NSError error) => {
				if(error != null && error.Code == (long)CKErrorCode.PartialFailure) {
					CKErrorInfo info = new CKErrorInfo(error);
					error = info[imgRecordId];
				}

				Error errorResponse = HandleError(error);
				switch(errorResponse) {
					case Error.Success:
						CKRecord fetchedImageRecord = (CKRecord)recordDict[imgRecordId];
						ImageRecord = new Image(fetchedImageRecord);
						if(updateBlock != null)
							updateBlock();
						break;

					case Error.Retry:
						Utils.Retry(() => LoadImage(keys, updateBlock), error);
						ImageRecord = null;
						break;

					case Error.Ignore:
						Console.WriteLine ("Error: {0}", error.Description);
						ImageRecord = null;
						break;

					default:
						throw new NotImplementedException();
				}
			};
			PublicDB.AddOperation (imageOp);
		}
		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);
		}