Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent duplicate posts in Post List #6102

Merged
merged 9 commits into from
Oct 24, 2016
Merged

Conversation

astralbodies
Copy link
Contributor

Original issue:
Sometimes when publishing a new post a duplicate post will appear in the post list. It was determined that the cause of this is the dismissal of the editor before the post has finished being published. The post list view controller initiated a refresh of posts before the post saves properly to Core Data and then a duplicate appears. If the posting was done within the timeout period of the list view controller (less than 5 minutes) then the issue wouldn't present itself.

Several improvements were made:

  1. Added KVC life cycle calls to AbstractPost to prevent any possible issues with accessing original and revision.
  2. Updated the filter on the post list to only show "original" posts. The filter was showing original and revision posts which can add to the confusion.
  3. Added a method to MediaService to allow for uploading of multiple media and not calling success until they have all finished.
  4. Prevent the editor UI from being dismissed until the publishing (and updating of media) has completed. Added another HUD to indicate an operation is in progress.

To test:

  1. Change postsControllerRefreshInterval in AbstractPostListViewController to a low value like 1.
  2. Publish a new post.
  3. Verify no duplicate posts appear in the list.

Variations of "new post" should include:

  • Published and Draft posts.
  • With and without media.
  • Single and multiple media.

We should also try offline condition testing as I've slightly altered behavior with the failure handlers.

Needs review: @SergioEstevao @nheagy @diegoreymendez @jleandroperez


for (Media *media in mediaObjects) {
// This effectively ignores any errors presented
[self updateMedia:media success:individualOperationSuccess failure:individualOperationSuccess];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ignore the errors at the service level? I prefer to leave that to whom invokes the method. If we have the failure callback we should make use of it.
If not the remove the failure callback and tell in the method comment that all errors are ignored and you callback when all operations finish independently of state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agreed - I'll fix this up.

@diegoreymendez
Copy link
Contributor

diegoreymendez commented Oct 21, 2016

  1. Updated the filter on the post list to only show "original" posts. The filter was showing original and revision posts which can add to the confusion.

One of the reasons why revision posts were being shown in the main list is that they offered visual cues that the post had local changes (that weren't uploaded to the server).

Here's how a post with local / unsaved changes looked before:

screen shot 2016-10-20 at 9 14 41 pm

Here's how it looks now:

screen shot 2016-10-20 at 9 06 38 pm

@@ -292,12 +294,20 @@ - (AbstractPost *)latest

- (AbstractPost *)revision
{
return [self primitiveValueForKey:@"revision"];
[self willAccessValueForKey:@"revision"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a vanilla implementation of the CoreData version of this getter. The same applies to the getter for original.

I can't recall if this was there for a reason, but it sounds to me like we could simply remove these methods.

Thoughts on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try replacing the read-only getter with a read/write and drop the getter code. For some reason it majorly screwed up the filtering in the post list view controller. I didn't have time to dig down that rabbit hole so I reverted that change and put the KVC method calls in here.

[self setPrimitiveValue:nil forKey:@"revision"];
[self didChangeValueForKey:@"revision"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know... THIS was precisely the root of a Simplenote bug which took us months to reproduce.

YAY for this change!

@jleandroperez
Copy link
Contributor

UX decisions aside (whether to show Local or not), stepping by to confirm the fix.

IMHO it's way better not to display duplicated entries, rather than rendering extra post details (Local Modifications).

:shipit: on my side!!

@@ -251,6 +251,7 @@ class PostListViewController : AbstractPostListViewController, UIViewControllerR
var predicates = [NSPredicate]()

if let blog = blog {
// Show all original posts without a revision & revision posts.
let basePredicate = NSPredicate(format: "blog = %@ && revision = nil", blog)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diegoreymendez I reverted the change here and added a comment to emphasize the importance of why revision was selected. 😄

if (overallSuccess && failedOperations != totalOperations.unsignedIntegerValue) {
overallSuccess();
} else if (failure && failedOperations == totalOperations.unsignedIntegerValue) {
NSError *error = [NSError errorWithDomain:WordPressComRestApiErrorDomain
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SergioEstevao I added a condition that if all of the operations fail that the error completion handler is called. This matches my original intended behavior as described in the header of the class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@jleandroperez jleandroperez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Github: PLEASE fix your UI

:shipit:

if (overallSuccess && failedOperations != totalOperations.unsignedIntegerValue) {
overallSuccess();
} else if (failure && failedOperations == totalOperations.unsignedIntegerValue) {
NSError *error = [NSError errorWithDomain:WordPressComRestApiErrorDomain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@diegoreymendez diegoreymendez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @astralbodies!

Copy link
Contributor

@nheagy nheagy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see this cleaned up!

:shipit:

hudText = NSLocalizedString(@"Publishing...", @"Text displayed in HUD while a post is being published.");
} else {
hudText = NSLocalizedString(@"Saving...", @"Text displayed in HUD while a post is being saved as a draft.");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice UX improvement here. This fixes #6079?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nheagy It does not fix this behavior if this code is broken. We definitely need to work on a state machine for this status stuff - it's super confusing.

@astralbodies
Copy link
Contributor Author

Thanks everyone for the reviews!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants