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

Show a toast message (notice) when moving posts to trash #21724

Merged
merged 12 commits into from
Oct 27, 2023

Conversation

crazytonyli
Copy link
Contributor

@crazytonyli crazytonyli commented Oct 10, 2023

For context, this change is initiated from this conversation: p1694476848843439-slack-C0180B5PRJ4

Changes

This PR changes how to present an "undo" action to users when moving a post to trash. Here is a video preview of before & after the change.

Before (with an undo row) After (with a toast message)
undo.mp4
toast.mp4

Problems of the "Undo" row

From design perspective, there are a couple of issues:

  1. This feature here is the only one in the app that shows an "undo" row. In other actions in the posts list and other areas in the app, a toast message with an action button is commonly used.
  2. After moving many posts to trash, the trashed posts' corresponding "undo" row has exactly the same appearance which is not intuitive to users when they do want to undo the previous trash action.

From implementation perspective, it's not straight-forward to implement keeping the trashed post position and placing a "undo" row in it. From a quick testing, the existing implementation has an obvious bug when trashing posts from bottom to up.

New flow

As you can see in the video, the new design is showing a toast message with an "Undo" button, which is consistent with other actions like restoring post from trash.

Implementation details

There are three parts involved in this PR

1. Removing the restorableStatus stored property from AbstractPost

This property is used to kept the original post status when it's moved to trash.

One obvious issue is, this property is not a "managed" property, which means it's not stored into the Core Data database. So, its value is guaranteed to be lost if multiple context objects are used to write and read this information.

Also, this property gives a false sense of a trashed post can be "restored" to any status, but I believe in reality a trashed post can only be restored to draft.

2. Use delete, trash, restore functions in PostRepository

These functions a newly implemented in PostRepository, as replacements for the ones in PostService which are now deleted in this PR.

3. Show toast messages after trashing posts

This is the UI change part of this PR.

Test Instructions

Here are a few things that I can think of:

  1. Verify the "Undo" button can restore the trashed post as draft.
  2. Verify that there is no side-effect in perform "Move to trash" action multiple times on the same post.
  3. Verify trashing many posts at the same time works.

Regression Notes

  1. Potential unintended areas of impact
    None.

  2. What I did to test those areas of impact (or what existing automated tests I relied on)
    What's described in the "Test Instructions" section.

  3. What automated tests I added (or what prevented me from doing so)
    None.

PR submission checklist:

  • I have completed the Regression Notes.
  • I have considered adding unit tests for my changes.
  • I have considered adding accessibility improvements for my changes.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

UI Changes testing checklist:

  • Portrait and landscape orientations.
  • Light and dark modes.
  • Fonts: Larger, smaller and bold text.
  • High contrast.
  • VoiceOver.
  • Languages with large words or with letters/accents not frequently used in English.
  • Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
  • iPhone and iPad.
  • Multi-tasking: Split view and Slide over. (iPad)

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Oct 10, 2023

WordPress Alpha📲 You can test the changes from this Pull Request in WordPress Alpha by scanning the QR code below to install the corresponding build.
App NameWordPress Alpha WordPress Alpha
ConfigurationRelease-Alpha
Build Numberpr21724-97ff26c
Version23.5
Bundle IDorg.wordpress.alpha
Commit97ff26c
App Center BuildWPiOS - One-Offs #7570
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Oct 10, 2023

Jetpack Alpha📲 You can test the changes from this Pull Request in Jetpack Alpha by scanning the QR code below to install the corresponding build.
App NameJetpack Alpha Jetpack Alpha
ConfigurationRelease-Alpha
Build Numberpr21724-97ff26c
Version23.5
Bundle IDcom.jetpack.alpha
Commit97ff26c
App Center Buildjetpack-installable-builds #6599
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@peril-wordpress-mobile
Copy link

peril-wordpress-mobile bot commented Oct 11, 2023

Warnings
⚠️ This PR is assigned to a milestone which is closing in less than 4 days Please, make sure to get it merged by then or assign it to a later expiring milestone
⚠️ PR has more than 500 lines of code changing. Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS

@crazytonyli crazytonyli added this to the 23.5 milestone Oct 11, 2023
@crazytonyli crazytonyli marked this pull request as ready for review October 11, 2023 00:21
Copy link
Contributor

@mokagio mokagio left a comment

Choose a reason for hiding this comment

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

Left a few questions.

Comment on lines 83 to +86

let status = try await coreDataStack.performQuery { try $0.existingObject(with: postID).status }
assert(status == .trash, "This function can only be used to delete trashed posts/pages.")

Copy link
Contributor

Choose a reason for hiding this comment

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

At first I was surprised to see the assert here and not at the start of the method. But I guess the reason is that the code above recur to find the original post, and for the same reason we want to delete the original, we want to check the status on it. Am I reading this right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. This status check doesn't exist in the original delete function in PostService. I added it here to catch incorrect calls to this function (which may never happen).

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

let postObjectID = apost.objectID
@MainActor
func deletePost(_ apost: AbstractPost) async {
assert(apost.managedObjectContext == ContextManager.shared.mainContext)
Copy link
Contributor

Choose a reason for hiding this comment

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

Out of scope: I've been thinking about all the accesses to ContextManager.shared that we have and the issues they sometime cause such as #21742.

Would it make sense to instantiate the view controller with a context or CoreDataStack object? In practice, this would be ContextManager.shared.mainContext or ContextManager.shared but the separation would:

  1. Centralize the definition of the shared resource access
  2. Allow us to replace it in the tests

What do you think?

Comment on lines 966 to 967
let message = NSLocalizedString("Post moved to trash.", comment: "A short message explaining that a post was moved to the trash bin.")
let undoAction = NSLocalizedString("Undo", comment: "The title of an 'undo' button. Tapping the button moves a trashed post out of the trash folder.")
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be a good time to introduce reverse-DNS keys in these localized strings. For context: #19028

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you

}
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered moving some of the logic here into a ViewModel-like object, so that we can test it?

One point of friction I can see is that there is no such object for this VC that I can see at the moment. Another is that this is a base class, meaning that the actual subclasses the app uses at runtime might need additional behavior and might be left with having to create their own ViewModel subclasses. This would be confusing. At the same time, it would be a sign of an abstraction that no longer serves us well and we might want to update its design.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That'd be much better than this view controller's current state. But I don't want to just move this small part (trashing, deleting, restoring) out, leaving this view controller in a weird state with mixed-patterns. A large refactor on this screen also seems unnecessary at this point.

Copy link
Contributor

Choose a reason for hiding this comment

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

leaving this view controller in a weird state with mixed-patterns

Good point 🤔

I am a fan of sprouting (a la Working Effectively with Legacy Code) better code from existing code, even if what is added/extracted is a little portion of the total. But ☝️ having mixed patterns is a concern.

I guess it would be acceptable only if we had an agreed upon expectation that "all new and modified code should follow ." That is not the case at the moment.

@crazytonyli crazytonyli modified the milestones: 23.5, 23.6 Oct 15, 2023
Copy link
Contributor

@mokagio mokagio left a comment

Choose a reason for hiding this comment

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

Given this PR has been sitting idle waiting for my feedback, the least I could do was fixing the merge conflict (on RELEASE-NOTES.txt) to unblock it's merge

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.

3 participants