diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 9e105d5f0caf..9036a659aafd 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,7 +1,7 @@ 23.6 ----- - * [***] [internal][Jetpack-only] [***] Added paid domain selection, plan selection, and checkout screens in site creation flow [#21688] +* [**] When moving a post to trash, show a toast message with undo action instead of an inline undo row. [#21724] * [*] Site Domains: Fixed an issue where the message shared while adding a domain was inaccurate. [#21827] * [*] Fix an issue where login with site address is blocked after failing the first attempt. [#21848] * [*] Fix an issue with an issue [#16999] with HTML not being stripped from post titles [#21846] diff --git a/WordPress/Classes/Models/AbstractPost.h b/WordPress/Classes/Models/AbstractPost.h index eb780151c39e..571a7c62181a 100644 --- a/WordPress/Classes/Models/AbstractPost.h +++ b/WordPress/Classes/Models/AbstractPost.h @@ -36,10 +36,6 @@ typedef NS_ENUM(NSUInteger, AbstractPostRemoteStatus) { // These are primarily used as helpers sorting fetchRequests. @property (nonatomic, assign) BOOL metaIsLocal; @property (nonatomic, assign) BOOL metaPublishImmediately; -/** - Used to store the post's status before its sent to the trash. - */ -@property (nonatomic, strong) NSString *restorableStatus; /** This array will contain a list of revision IDs. */ diff --git a/WordPress/Classes/Models/AbstractPost.m b/WordPress/Classes/Models/AbstractPost.m index 1e62605b84c2..a17a917c4ab2 100644 --- a/WordPress/Classes/Models/AbstractPost.m +++ b/WordPress/Classes/Models/AbstractPost.m @@ -39,8 +39,6 @@ @implementation AbstractPost @dynamic autosaveModifiedDate; @dynamic autosaveIdentifier; -@synthesize restorableStatus; - + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; diff --git a/WordPress/Classes/Services/PostRepository.swift b/WordPress/Classes/Services/PostRepository.swift index c09d99d12b20..13b58dd1c1d1 100644 --- a/WordPress/Classes/Services/PostRepository.swift +++ b/WordPress/Classes/Services/PostRepository.swift @@ -81,6 +81,9 @@ final class PostRepository { return } + 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.") + // First delete the post from local database. let (remote, remotePost) = try await coreDataStack.performAndSave { [remoteFactory] context in let post = try context.existingObject(with: postID) diff --git a/WordPress/Classes/Services/PostService.h b/WordPress/Classes/Services/PostService.h index 0082dcf00f4b..7fff3d5d7c6b 100644 --- a/WordPress/Classes/Services/PostService.h +++ b/WordPress/Classes/Services/PostService.h @@ -125,41 +125,6 @@ forceDraftIfCreating:(BOOL)forceDraftIfCreating success:(nullable void (^)(AbstractPost *post, NSString *previewURL))success failure:(void (^)(NSError * _Nullable error))failure; -/** - Attempts to delete the specified post outright vs moving it to the - trash folder. - - @param post The post or page to delete - @param success A success block - @param failure A failure block - */ -- (void)deletePost:(AbstractPost *)post - success:(nullable void (^)(void))success - failure:(void (^)(NSError * _Nullable error))failure; - -/** - Moves the specified post into the trash bin. Does not delete - the post unless it was deleted on the server. - - @param post The post or page to trash - @param success A success block - @param failure A failure block - */ -- (void)trashPost:(AbstractPost *)post - success:(nullable nullable void (^)(void))success - failure:(void (^)(NSError * _Nullable error))failure; - -/** - Moves the specified post out of the trash bin. - - @param post The post or page to restore - @param success A success block - @param failure A failure block - */ -- (void)restorePost:(AbstractPost *)post - success:(nullable void (^)(void))success - failure:(void (^)(NSError * _Nullable error))failure; - @end NS_ASSUME_NONNULL_END diff --git a/WordPress/Classes/Services/PostService.m b/WordPress/Classes/Services/PostService.m index e9fb06a08bba..a3c5e407e1aa 100644 --- a/WordPress/Classes/Services/PostService.m +++ b/WordPress/Classes/Services/PostService.m @@ -449,195 +449,6 @@ - (void)handleAutoSaveWithRestRemote:(PostServiceRemoteREST *)restRemote } -#pragma mark - Delete, Trashing, Restoring - -- (void)deletePost:(AbstractPost *)post - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - void (^privateBlock)(void) = ^void() { - NSNumber *postID = post.postID; - if ([postID longLongValue] > 0) { - RemotePost *remotePost = [PostHelper remotePostWithPost:post]; - id remote = [self.postServiceRemoteFactory forBlog:post.blog]; - [remote deletePost:remotePost success:success failure:failure]; - } - [self.managedObjectContext deleteObject:post]; - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; - }; - - if ([post isRevision]) { - [self deletePost:post.original success:privateBlock failure:failure]; - } else { - privateBlock(); - } -} - -- (void)trashPost:(AbstractPost *)post - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - if ([post.status isEqualToString:PostStatusTrash]) { - [self deletePost:post success:success failure:failure]; - return; - } - - void(^privateBodyBlock)(void) = ^void() { - post.restorableStatus = post.status; - - NSNumber *postID = post.postID; - - if ([post isRevision] || [postID longLongValue] <= 0) { - post.status = PostStatusTrash; - - if (success) { - success(); - } - - return; - } - - [self trashRemotePostWithPost:post - success:success - failure:failure]; - }; - - if ([post isRevision]) { - [self trashPost:post.original - success:privateBodyBlock - failure:failure]; - } else { - privateBodyBlock(); - } -} - -- (void)trashRemotePostWithPost:(AbstractPost*)post - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSManagedObjectID *postObjectID = post.objectID; - - void (^successBlock)(RemotePost *post) = ^(RemotePost *remotePost) { - NSError *err; - Post *postInContext = (Post *)[self.managedObjectContext existingObjectWithID:postObjectID error:&err]; - if (err) { - DDLogError(@"%@", err); - } - if (postInContext) { - if (!remotePost || [remotePost.status isEqualToString:PostStatusDeleted]) { - [self.managedObjectContext deleteObject:post]; - } else { - [PostHelper updatePost:postInContext withRemotePost:remotePost inContext:self.managedObjectContext]; - postInContext.latest.statusAfterSync = postInContext.statusAfterSync; - postInContext.latest.status = postInContext.status; - } - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; - } - if (success) { - success(); - } - }; - - void (^failureBlock)(NSError *error) = ^(NSError *error) { - NSError *err; - Post *postInContext = (Post *)[self.managedObjectContext existingObjectWithID:postObjectID error:&err]; - if (err) { - DDLogError(@"%@", err); - } - if (postInContext) { - postInContext.restorableStatus = nil; - } - if (failure){ - failure(error); - } - }; - - RemotePost *remotePost = [PostHelper remotePostWithPost:post]; - id remote = [self.postServiceRemoteFactory forBlog:post.blog]; - [remote trashPost:remotePost success:successBlock failure:failureBlock]; -} - -- (void)restorePost:(AbstractPost *)post - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - void (^privateBodyBlock)(void) = ^void() { - post.status = post.restorableStatus; - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; - - if (![post isRevision] && [post.postID longLongValue] > 0) { - [self restoreRemotePostWithPost:post success:success failure:failure]; - } else { - if (success) { - success(); - } - } - }; - - if (post.isRevision) { - [self restorePost:post.original - success:privateBodyBlock - failure:failure]; - - return; - } else { - privateBodyBlock(); - } -} - -- (void)restoreRemotePostWithPost:(AbstractPost*)post - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSManagedObjectID *postObjectID = post.objectID; - - void (^successBlock)(RemotePost *post) = ^(RemotePost *remotePost) { - NSError *err; - Post *postInContext = (Post *)[self.managedObjectContext existingObjectWithID:postObjectID error:&err]; - postInContext.restorableStatus = nil; - if (err) { - DDLogError(@"%@", err); - } - if (postInContext) { - [PostHelper updatePost:postInContext withRemotePost:remotePost inContext:self.managedObjectContext]; - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; - } - if (success) { - success(); - } - }; - - void (^failureBlock)(NSError *error) = ^(NSError *error) { - NSError *err; - Post *postInContext = (Post *)[self.managedObjectContext existingObjectWithID:postObjectID error:&err]; - if (err) { - DDLogError(@"%@", err); - } - if (postInContext) { - // Put the post back in the trash bin. - postInContext.status = PostStatusTrash; - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; - } - if (failure){ - failure(error); - } - }; - - RemotePost *remotePost = [PostHelper remotePostWithPost:post]; - if (post.restorableStatus) { - remotePost.status = post.restorableStatus; - } else { - // Assign a status of draft to the remote post. The WordPress.com REST API will - // ignore this and should restore the post's previous status. The XML-RPC API - // needs a status assigned to move a post out of the trash folder. Draft is the - // safest option when we don't know what the status was previously. - remotePost.status = PostStatusDraft; - } - - id remote = [self.postServiceRemoteFactory forBlog:post.blog]; - [remote restorePost:remotePost success:successBlock failure:failureBlock]; -} - #pragma mark - Helpers - (NSDictionary *)remoteSyncParametersDictionaryForRemote:(nonnull id )remote diff --git a/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift b/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift index 80f26c4c05c9..4a3712e88124 100644 --- a/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift +++ b/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift @@ -17,8 +17,6 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe static let pagesViewControllerRestorationKey = "PagesViewControllerRestorationKey" static let pageCellIdentifier = "PageCellIdentifier" static let pageCellNibName = "PageListTableViewCell" - static let restorePageCellIdentifier = "RestorePageCellIdentifier" - static let restorePageCellNibName = "RestorePageTableViewCell" static let templatePageCellIdentifier = "TemplatePageCellIdentifier" static let currentPageListStatusFilterKey = "CurrentPageListStatusFilterKey" } @@ -204,9 +202,6 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe let pageCellNib = UINib(nibName: Constant.Identifiers.pageCellNibName, bundle: bundle) tableView.register(pageCellNib, forCellReuseIdentifier: Constant.Identifiers.pageCellIdentifier) - let restorePageCellNib = UINib(nibName: Constant.Identifiers.restorePageCellNibName, bundle: bundle) - tableView.register(restorePageCellNib, forCellReuseIdentifier: Constant.Identifiers.restorePageCellIdentifier) - tableView.register(TemplatePageTableViewCell.self, forCellReuseIdentifier: Constant.Identifiers.templatePageCellIdentifier) WPStyleGuide.configureColors(view: view, tableView: tableView) @@ -425,16 +420,7 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe let searchText = currentSearchTerm() ?? "" let filterPredicate = searchController.isActive ? NSPredicate(format: "postTitle CONTAINS[cd] %@", searchText) : filterSettings.currentPostListFilter().predicateForFetchRequest - // If we have recently trashed posts, create an OR predicate to find posts matching the filter, - // or posts that were recently deleted. - if searchText.count == 0 && recentlyTrashedPostObjectIDs.count > 0 { - - let trashedPredicate = NSPredicate(format: "SELF IN %@", recentlyTrashedPostObjectIDs) - - predicates.append(NSCompoundPredicate(orPredicateWithSubpredicates: [filterPredicate, trashedPredicate])) - } else { - predicates.append(filterPredicate) - } + predicates.append(filterPredicate) if searchText.count > 0 { let searchPredicate = NSPredicate(format: "postTitle CONTAINS[cd] %@", searchText) @@ -516,10 +502,7 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe return cell } - let page = pageAtIndexPath(indexPath) - - let identifier = cellIdentifierForPage(page) - let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) + let cell = tableView.dequeueReusableCell(withIdentifier: Constant.Identifiers.pageCellIdentifier, for: indexPath) configureCell(cell, at: indexPath) return cell @@ -541,11 +524,8 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe cell.onAction = { [weak self] cell, button, page in self?.handleMenuAction(fromCell: cell, fromButton: button, forPage: page) } - } else if cell.reuseIdentifier == Constant.Identifiers.restorePageCellIdentifier { - cell.selectionStyle = .none - cell.onAction = { [weak self] cell, _, page in - self?.handleRestoreAction(fromCell: cell, forPage: page) - } + } else { + assertionFailure("Unknown cell: \(cell)") } cell.contentView.backgroundColor = UIColor.listForeground @@ -553,18 +533,6 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe cell.configureCell(page) } - fileprivate func cellIdentifierForPage(_ page: Page) -> String { - var identifier: String - - if recentlyTrashedPostObjectIDs.contains(page.objectID) == true && filterSettings.currentPostListFilter().filterType != .trashed { - identifier = Constant.Identifiers.restorePageCellIdentifier - } else { - identifier = Constant.Identifiers.pageCellIdentifier - } - - return identifier - } - // MARK: - Post Actions override func createPost() { @@ -624,24 +592,14 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe fileprivate func draftPage(_ apost: AbstractPost, at indexPath: IndexPath?) { WPAnalytics.track(.postListDraftAction, withProperties: propertiesForAnalytics()) - let previousStatus = apost.status - apost.status = .draft - - let contextManager = ContextManager.sharedInstance() - let postService = PostService(managedObjectContext: contextManager.mainContext) - - postService.uploadPost(apost, success: { [weak self] _ in - DispatchQueue.main.async { + let repository = PostRepository(coreDataStack: ContextManager.shared) + Task { @MainActor [weak self] in + do { + try await repository.restore(TaggedManagedObjectID(apost), to: .draft) self?._tableViewHandler.refreshTableView(at: indexPath) + } catch { + WPError.showXMLRPCErrorAlert(error) } - }) { [weak self] (error) in - apost.status = previousStatus - - if let strongSelf = self { - contextManager.save(strongSelf.managedObjectContext()) - } - - WPError.showXMLRPCErrorAlert(error) } } @@ -815,10 +773,6 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe } } - override func deletePost(_ apost: AbstractPost) { - super.deletePost(apost) - } - private func addBlazeAction(to controller: UIAlertController, for page: AbstractPost) { guard BlazeHelper.isBlazeFlagEnabled() && page.canBlaze else { return @@ -927,12 +881,6 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe return page } - fileprivate func handleRestoreAction(fromCell cell: UITableViewCell, forPage page: AbstractPost) { - restorePost(page) { [weak self] in - self?._tableViewHandler.refreshTableView(at: self?.tableView.indexPath(for: cell)) - } - } - private func addSetHomepageAction(to controller: UIAlertController, for page: AbstractPost, at index: IndexPath?) { let objectID = page.objectID @@ -1040,7 +988,7 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe alertController.addCancelActionWithTitle(cancelText) alertController.addDestructiveActionWithTitle(deleteText) { [weak self] action in - self?.deletePost(post) + Task { await self?.deletePost(post) } } alertController.presentFromRootViewController() } diff --git a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.h b/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.h deleted file mode 100644 index 1710b0acb21e..000000000000 --- a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import "BasePageListCell.h" - -@interface RestorePageTableViewCell : BasePageListCell - -@end diff --git a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.m b/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.m deleted file mode 100644 index c05e0d4ce98c..000000000000 --- a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.m +++ /dev/null @@ -1,42 +0,0 @@ -#import "RestorePageTableViewCell.h" -#import "WPStyleGuide+Pages.h" - -@import Gridicons; - -@interface RestorePageTableViewCell() - -@property (nonatomic, strong) IBOutlet UILabel *restoreLabel; -@property (nonatomic, strong) IBOutlet UIButton *restoreButton; - -@end - -@implementation RestorePageTableViewCell - -#pragma mark - Life Cycle - -- (void)awakeFromNib { - [super awakeFromNib]; - - [self configureView]; - [self applyStyles]; -} - -#pragma mark - Configuration - -- (void)applyStyles -{ - [WPStyleGuide applyRestorePageLabelStyle:self.restoreLabel]; - [WPStyleGuide applyRestorePageButtonStyle:self.restoreButton]; -} - -- (void)configureView -{ - self.restoreLabel.text = NSLocalizedString(@"Page moved to trash.", @"A short message explaining that a page was moved to the trash bin."); - NSString *buttonTitle = NSLocalizedString(@"Undo", @"The title of an 'undo' button. Tapping the button moves a trashed page out of the trash folder."); - [self.restoreButton setTitle:buttonTitle forState:UIControlStateNormal]; - [self.restoreButton setImage:[UIImage gridiconOfType:GridiconTypeUndo - withSize:CGSizeMake(18.0, 18.0)] - forState:UIControlStateNormal]; -} - -@end diff --git a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.xib b/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.xib deleted file mode 100644 index 2904556a70cb..000000000000 --- a/WordPress/Classes/ViewRelated/Pages/RestorePageTableViewCell.xib +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift index c1892e233ff9..f503e545a0e9 100644 --- a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift @@ -130,7 +130,6 @@ class AbstractPostListViewController: UIViewController, @IBOutlet var filterTabBar: FilterTabBar! @objc var searchController: UISearchController! - @objc var recentlyTrashedPostObjectIDs = [NSManagedObjectID]() // IDs of trashed posts. Cleared on refresh or when filter changes. fileprivate var searchesSyncing = 0 @@ -673,10 +672,6 @@ class AbstractPostListViewController: UIViewController, } func syncHelper(_ syncHelper: WPContentSyncHelper, syncContentWithUserInteraction userInteraction: Bool, success: ((_ hasMore: Bool) -> ())?, failure: ((_ error: NSError) -> ())?) { - if recentlyTrashedPostObjectIDs.count > 0 { - refreshAndReload() - } - let postType = postTypeToSync() let filter = filterSettings.currentPostListFilter() let author = filterSettings.shouldShowOnlyMyPosts() ? blogUserID() : nil @@ -985,12 +980,11 @@ class AbstractPostListViewController: UIViewController, navigationController?.present(navWrapper, animated: true) } - @objc func deletePost(_ apost: AbstractPost) { - WPAnalytics.track(.postListTrashAction, withProperties: propertiesForAnalytics()) - - let postObjectID = apost.objectID + @MainActor + func deletePost(_ apost: AbstractPost) async { + assert(apost.managedObjectContext == ContextManager.shared.mainContext) - recentlyTrashedPostObjectIDs.append(postObjectID) + WPAnalytics.track(.postListTrashAction, withProperties: propertiesForAnalytics()) // Remove the trashed post from spotlight SearchManager.shared.deleteSearchableItem(apost) @@ -998,111 +992,81 @@ class AbstractPostListViewController: UIViewController, // Update the fetch request *before* making the service call. updateAndPerformFetchRequest() - let indexPath = tableViewHandler.resultsController?.indexPath(forObject: apost) - - if let indexPath = indexPath { - tableView.reloadRows(at: [indexPath], with: .fade) - } - - let postService = PostService(managedObjectContext: ContextManager.sharedInstance().mainContext) + let originalStatus = apost.status let trashed = (apost.status == .trash) - postService.trashPost(apost, success: { - // If we permanently deleted the post + let repository = PostRepository(coreDataStack: ContextManager.shared) + do { + try await repository.trash(TaggedManagedObjectID(apost)) if trashed { PostCoordinator.shared.cancelAnyPendingSaveOf(post: apost) MediaCoordinator.shared.cancelUploadOfAllMedia(for: apost) } - }, failure: { [weak self] (error) in - guard let strongSelf = self else { - return - } - - if let error = error as NSError?, error.code == type(of: strongSelf).HTTPErrorCodeForbidden { - strongSelf.promptForPassword() + let message = NSLocalizedString("postsList.movePostToTrash.message", comment: "A short message explaining that a post was moved to the trash bin.") + let undoAction = NSLocalizedString("postsList.movePostToTrash.undo", comment: "The title of an 'undo' button. Tapping the button moves a trashed post out of the trash folder.") + let notice = Notice(title: message, actionTitle: undoAction, actionHandler: { [weak self] accepted in + if accepted { + self?.restorePost(apost, toStatus: originalStatus ?? .draft) + } + }) + ActionDispatcher.dispatch(NoticeAction.dismiss) + ActionDispatcher.dispatch(NoticeAction.post(notice)) + } catch { + if let error = error as NSError?, error.code == type(of: self).HTTPErrorCodeForbidden { + promptForPassword() } else { WPError.showXMLRPCErrorAlert(error) } - if let index = strongSelf.recentlyTrashedPostObjectIDs.firstIndex(of: postObjectID) { - strongSelf.recentlyTrashedPostObjectIDs.remove(at: index) - // We don't really know what happened here, why did the request fail? - // Maybe we could not delete the post or maybe the post was already deleted - // It is safer to re fetch the results than to reload that specific row - DispatchQueue.main.async { - strongSelf.updateAndPerformFetchRequestRefreshingResults() - } + // We don't really know what happened here, why did the request fail? + // Maybe we could not delete the post or maybe the post was already deleted + // It is safer to re fetch the results than to reload that specific row + DispatchQueue.main.async { + self.updateAndPerformFetchRequestRefreshingResults() } - }) + } } - @objc func restorePost(_ apost: AbstractPost, completion: (() -> Void)? = nil) { + func restorePost(_ apost: AbstractPost, toStatus status: BasePost.Status) { WPAnalytics.track(.postListRestoreAction, withProperties: propertiesForAnalytics()) // if the post was recently deleted, update the status helper and reload the cell to display a spinner let postObjectID = apost.objectID - if let index = recentlyTrashedPostObjectIDs.firstIndex(of: postObjectID) { - recentlyTrashedPostObjectIDs.remove(at: index) - } - if filterSettings.currentPostListFilter().filterType != .draft { // Needed or else the post will remain in the published list. updateAndPerformFetchRequest() tableView.reloadData() } - let postService = PostService(managedObjectContext: ContextManager.sharedInstance().mainContext) + let repository = PostRepository(coreDataStack: ContextManager.shared) + Task { @MainActor in + do { + try await repository.restore(.init(apost), to: status) - postService.restore(apost, success: { [weak self] in + if let postStatus = apost.status { + // If the post was restored, see if it appears in the current filter. + // If not, prompt the user to let it know under which filter it appears. + let filter = filterSettings.filterThatDisplaysPostsWithStatus(postStatus) - guard let strongSelf = self else { - return - } + if filter.filterType == filterSettings.currentPostListFilter().filterType { + return + } - var apost: AbstractPost + promptThatPostRestoredToFilter(filter) - // Make sure the post still exists. - do { - apost = try strongSelf.managedObjectContext().existingObject(with: postObjectID) as! AbstractPost + // Reindex the restored post in spotlight + SearchManager.shared.indexItem(apost) + } } catch { - DDLogError("\(error)") - return - } - - DispatchQueue.main.async { - completion?() - } - - if let postStatus = apost.status { - // If the post was restored, see if it appears in the current filter. - // If not, prompt the user to let it know under which filter it appears. - let filter = strongSelf.filterSettings.filterThatDisplaysPostsWithStatus(postStatus) - - if filter.filterType == strongSelf.filterSettings.currentPostListFilter().filterType { - return + if let error = error as NSError?, error.code == AbstractPostListViewController.HTTPErrorCodeForbidden { + promptForPassword() + } else { + WPError.showXMLRPCErrorAlert(error) } - - strongSelf.promptThatPostRestoredToFilter(filter) - - // Reindex the restored post in spotlight - SearchManager.shared.indexItem(apost) - } - }) { [weak self] (error) in - - guard let strongSelf = self else { - return } - - if let error = error as NSError?, error.code == type(of: strongSelf).HTTPErrorCodeForbidden { - strongSelf.promptForPassword() - } else { - WPError.showXMLRPCErrorAlert(error) - } - - strongSelf.recentlyTrashedPostObjectIDs.append(postObjectID) } } @@ -1145,7 +1109,6 @@ class AbstractPostListViewController: UIViewController, // MARK: - Filtering @objc func refreshAndReload() { - recentlyTrashedPostObjectIDs.removeAll() updateSelectedFilter() resetTableViewContentOffset() updateAndPerformFetchRequestRefreshingResults() diff --git a/WordPress/Classes/ViewRelated/Post/InteractivePostViewDelegate.swift b/WordPress/Classes/ViewRelated/Post/InteractivePostViewDelegate.swift index 6e169d6f0da4..72359a09ea17 100644 --- a/WordPress/Classes/ViewRelated/Post/InteractivePostViewDelegate.swift +++ b/WordPress/Classes/ViewRelated/Post/InteractivePostViewDelegate.swift @@ -7,7 +7,6 @@ protocol InteractivePostViewDelegate: AnyObject { func duplicate(_ post: AbstractPost) func publish(_ post: AbstractPost) func trash(_ post: AbstractPost) - func restore(_ post: AbstractPost) func draft(_ post: AbstractPost) func retry(_ post: AbstractPost) func cancelAutoUpload(_ post: AbstractPost) diff --git a/WordPress/Classes/ViewRelated/Post/PostListViewController.swift b/WordPress/Classes/ViewRelated/Post/PostListViewController.swift index e66e91b6f2a4..782f96745d87 100644 --- a/WordPress/Classes/ViewRelated/Post/PostListViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/PostListViewController.swift @@ -8,10 +8,8 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe private let postCompactCellIdentifier = "PostCompactCellIdentifier" private let postCardTextCellIdentifier = "PostCardTextCellIdentifier" - private let postCardRestoreCellIdentifier = "PostCardRestoreCellIdentifier" private let postCompactCellNibName = "PostCompactCell" private let postCardTextCellNibName = "PostCardCell" - private let postCardRestoreCellNibName = "RestorePostTableViewCell" private let statsStoryboardName = "SiteStats" private let currentPostListStatusFilterKey = "CurrentPostListStatusFilterKey" private var postCellIdentifier: String { @@ -285,9 +283,6 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe let postCompactCellNib = UINib(nibName: postCompactCellNibName, bundle: bundle) tableView.register(postCompactCellNib, forCellReuseIdentifier: postCompactCellIdentifier) - let postCardRestoreCellNib = UINib(nibName: postCardRestoreCellNibName, bundle: bundle) - tableView.register(postCardRestoreCellNib, forCellReuseIdentifier: postCardRestoreCellIdentifier) - let headerNib = UINib(nibName: ActivityListSectionHeaderView.identifier, bundle: nil) tableView.register(headerNib, forHeaderFooterViewReuseIdentifier: ActivityListSectionHeaderView.identifier) @@ -421,15 +416,7 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe let searchText = currentSearchTerm() ?? "" let filterPredicate = searchController.isActive ? NSPredicate(format: "postTitle CONTAINS[cd] %@", searchText) : filterSettings.currentPostListFilter().predicateForFetchRequest - // If we have recently trashed posts, create an OR predicate to find posts matching the filter, - // or posts that were recently deleted. - if searchText.count == 0 && recentlyTrashedPostObjectIDs.count > 0 { - let trashedPredicate = NSPredicate(format: "SELF IN %@", recentlyTrashedPostObjectIDs) - - predicates.append(NSCompoundPredicate(orPredicateWithSubpredicates: [filterPredicate, trashedPredicate])) - } else { - predicates.append(filterPredicate) - } + predicates.append(filterPredicate) if filterSettings.shouldShowOnlyMyPosts() { let myAuthorID = blogUserID() ?? 0 @@ -468,9 +455,7 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe return windowlessCell } - let post = postAtIndexPath(indexPath) - let identifier = cellIdentifierForPost(post) - let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) + let cell = tableView.dequeueReusableCell(withIdentifier: postCellIdentifier, for: indexPath) configureCell(cell, at: indexPath) @@ -493,19 +478,6 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe configurablePostView.configure(with: post) configurePostCell(cell) - configureRestoreCell(cell) - } - - fileprivate func cellIdentifierForPost(_ post: Post) -> String { - var identifier: String - - if recentlyTrashedPostObjectIDs.contains(post.objectID) == true && filterSettings.currentPostListFilter().filterType != .trashed { - identifier = postCardRestoreCellIdentifier - } else { - identifier = postCellIdentifier - } - - return identifier } private func configurePostCell(_ cell: UITableViewCell) { @@ -516,14 +488,6 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe cell.shouldHideAuthor = showingJustMyPosts } - private func configureRestoreCell(_ cell: UITableViewCell) { - guard let cell = cell as? RestorePostTableViewCell else { - return - } - - cell.isCompact = isCompact - } - // MARK: - Post Actions override func createPost() { @@ -661,17 +625,11 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe alertController.addCancelActionWithTitle(cancelText) alertController.addDestructiveActionWithTitle(deleteText) { [weak self] action in - self?.deletePost(post) + Task { await self?.deletePost(post) } } alertController.presentFromRootViewController() } - func restore(_ post: AbstractPost) { - ReachabilityUtils.onAvailableInternetConnectionDo { - restorePost(post) - } - } - func draft(_ post: AbstractPost) { ReachabilityUtils.onAvailableInternetConnectionDo { moveToDraft(post) diff --git a/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.swift b/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.swift deleted file mode 100644 index 81ecf62681cd..000000000000 --- a/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit -import Gridicons - -class RestorePostTableViewCell: UITableViewCell, ConfigurablePostView, InteractivePostView { - @IBOutlet var postContentView: UIView! { - didSet { - postContentView.backgroundColor = .listForeground - } - } - @IBOutlet var restoreLabel: UILabel! - @IBOutlet var restoreButton: UIButton! - @IBOutlet var topMargin: NSLayoutConstraint! - - private weak var delegate: InteractivePostViewDelegate? - - var isCompact: Bool = false { - didSet { - isCompact ? configureCompact() : configureDefault() - } - } - var post: Post? - - func configure(with post: Post) { - self.post = post - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - // Don't respond to taps in margins. - if !postContentView.frame.contains(point) { - return nil - } - return super.hitTest(point, with: event) - } - - override func awakeFromNib() { - super.awakeFromNib() - - configureView() - applyStyles() - } - - private func configureView() { - restoreLabel.text = NSLocalizedString("Post moved to trash.", comment: "A short message explaining that a post was moved to the trash bin.") - let buttonTitle = NSLocalizedString("Undo", comment: "The title of an 'undo' button. Tapping the button moves a trashed post out of the trash folder.") - restoreButton.setTitle(buttonTitle, for: .normal) - restoreButton.setImage(.gridicon(.undo, size: CGSize(width: Constants.imageSize, - height: Constants.imageSize)), for: .normal) - } - - private func configureCompact() { - topMargin.constant = Constants.compactMargin - postContentView.layer.borderWidth = 0 - } - - private func configureDefault() { - topMargin.constant = Constants.defaultMargin - postContentView.layer.borderColor = WPStyleGuide.postCardBorderColor.cgColor - postContentView.layer.borderWidth = .hairlineBorderWidth - } - - private func applyStyles() { - WPStyleGuide.applyPostCardStyle(self) - WPStyleGuide.applyRestorePostLabelStyle(restoreLabel) - WPStyleGuide.applyRestorePostButtonStyle(restoreButton) - } - - @IBAction func restore(_ sender: Any) { - guard let post = post else { - return - } - - delegate?.restore(post) - } - - func setInteractionDelegate(_ delegate: InteractivePostViewDelegate) { - self.delegate = delegate - } - - func setActionSheetDelegate(_ delegate: PostActionSheetDelegate) { - // Do nothing, since this cell doesn't have an action to present an action sheet. - } - - private enum Constants { - static let defaultMargin: CGFloat = 16 - static let compactMargin: CGFloat = 0 - static let imageSize: CGFloat = 18.0 - } -} diff --git a/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.xib b/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.xib deleted file mode 100644 index 90a2bf15f525..000000000000 --- a/WordPress/Classes/ViewRelated/Post/RestorePostTableViewCell.xib +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 0e21990c23f2..64f14cc8a4cc 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -1298,7 +1298,6 @@ 57569CF2230485680052EE14 /* PostAutoUploadInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57569CF1230485680052EE14 /* PostAutoUploadInteractorTests.swift */; }; 575802132357C41200E4C63C /* MediaCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 575802122357C41200E4C63C /* MediaCoordinatorTests.swift */; }; 575E126322973EBB0041B3EB /* PostCompactCellGhostableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 575E126222973EBB0041B3EB /* PostCompactCellGhostableTests.swift */; }; - 575E126F229779E70041B3EB /* RestorePostTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 575E126E229779E70041B3EB /* RestorePostTableViewCell.swift */; }; 577C2AAB22936DCB00AD1F03 /* PostCardCellGhostableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 577C2AAA22936DCB00AD1F03 /* PostCardCellGhostableTests.swift */; }; 577C2AB422943FEC00AD1F03 /* PostCompactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 577C2AB322943FEC00AD1F03 /* PostCompactCell.swift */; }; 577C2AB62294401800AD1F03 /* PostCompactCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 577C2AB52294401800AD1F03 /* PostCompactCell.xib */; }; @@ -1346,12 +1345,9 @@ 5D1181E71B4D6DEB003F3084 /* WPStyleGuide+Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */; }; 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; }; 5D146EBB189857ED0068FDC6 /* FeaturedImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */; }; - 5D18FE9F1AFBB17400EFEED0 /* RestorePageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */; }; - 5D18FEA01AFBB17400EFEED0 /* RestorePageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */; }; 5D1D04751B7A50B100CDE646 /* Reader.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D1D04731B7A50B100CDE646 /* Reader.storyboard */; }; 5D1D04761B7A50B100CDE646 /* ReaderStreamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1D04741B7A50B100CDE646 /* ReaderStreamViewController.swift */; }; 5D2B30B91B7411C700DA15F3 /* ReaderCardDiscoverAttributionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2B30B81B7411C700DA15F3 /* ReaderCardDiscoverAttributionView.swift */; }; - 5D2FB2831AE98C4600F1D4ED /* RestorePostTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D2FB2821AE98C4600F1D4ED /* RestorePostTableViewCell.xib */; }; 5D3D559718F88C3500782892 /* ReaderPostService.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3D559618F88C3500782892 /* ReaderPostService.m */; }; 5D3E334E15EEBB6B005FC6F2 /* ReachabilityUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3E334D15EEBB6B005FC6F2 /* ReachabilityUtils.m */; }; 5D42A3DF175E7452005CFF05 /* AbstractPost.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D42A3D7175E7452005CFF05 /* AbstractPost.m */; }; @@ -4070,7 +4066,6 @@ FABB206D2602FC2C00C8785C /* NoteBlockUserTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B5C66B791ACF074600F68370 /* NoteBlockUserTableViewCell.xib */; }; FABB20702602FC2C00C8785C /* ReaderListStreamHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E6D2E1621B8AAA340000ED14 /* ReaderListStreamHeader.xib */; }; FABB20722602FC2C00C8785C /* MyProfileHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE1CCB2E2050502B000EE3AC /* MyProfileHeaderView.xib */; }; - FABB20742602FC2C00C8785C /* RestorePageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */; }; FABB20762602FC2C00C8785C /* WordPressShare.js in Resources */ = {isa = PBXBuildFile; fileRef = E1AFA8C21E8E34230004A323 /* WordPressShare.js */; }; FABB20772602FC2C00C8785C /* world-map.svg in Resources */ = {isa = PBXBuildFile; fileRef = 9A76C32E22AFDA2100F5D819 /* world-map.svg */; }; FABB20782602FC2C00C8785C /* NoteBlockTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B5C66B711ACF071000F68370 /* NoteBlockTextTableViewCell.xib */; }; @@ -4081,7 +4076,6 @@ FABB20862602FC2C00C8785C /* n.caf in Resources */ = {isa = PBXBuildFile; fileRef = 5D69DBC3165428CA00A2D1F7 /* n.caf */; }; FABB20882602FC2C00C8785C /* People.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E1B912801BB00EFD003C25B9 /* People.storyboard */; }; FABB208B2602FC2C00C8785C /* LatestPostSummaryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9826AE8121B5C6A700C851FA /* LatestPostSummaryCell.xib */; }; - FABB208D2602FC2C00C8785C /* RestorePostTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D2FB2821AE98C4600F1D4ED /* RestorePostTableViewCell.xib */; }; FABB208E2602FC2C00C8785C /* Plans.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1724DDCB1C6121D00099D273 /* Plans.storyboard */; }; FABB208F2602FC2C00C8785C /* PluginDirectoryCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40A2778020191B5E00D078D5 /* PluginDirectoryCollectionViewCell.xib */; }; FABB20902602FC2C00C8785C /* ReaderWelcomeBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BD8201824BCCE8600FF25FD /* ReaderWelcomeBanner.xib */; }; @@ -5144,7 +5138,6 @@ FABB25222602FC2C00C8785C /* MediaRequestAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1450CF22437DA3E00A28BFE /* MediaRequestAuthenticator.swift */; }; FABB25232602FC2C00C8785C /* StatsCellHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9881296C219CF1300075FF33 /* StatsCellHeader.swift */; }; FABB25242602FC2C00C8785C /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1823E6B1E42231C00C19F53 /* UIEdgeInsets.swift */; }; - FABB25252602FC2C00C8785C /* RestorePageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */; }; FABB25262602FC2C00C8785C /* String+RegEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54C02231F38F50100574572 /* String+RegEx.swift */; }; FABB25272602FC2C00C8785C /* UIImage+Exporters.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1FA9D1BF0EB840090C761 /* UIImage+Exporters.swift */; }; FABB25282602FC2C00C8785C /* PostStatsTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98FCFC212231DF43006ECDD4 /* PostStatsTitleCell.swift */; }; @@ -5257,7 +5250,6 @@ FABB259A2602FC2C00C8785C /* ReaderBlockSiteAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8212CC220AA7F57008E8AE8 /* ReaderBlockSiteAction.swift */; }; FABB259C2602FC2C00C8785C /* ReaderPostService+RelatedPosts.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA8E1F7625EEFA7300063673 /* ReaderPostService+RelatedPosts.swift */; }; FABB259D2602FC2C00C8785C /* Blog+Capabilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B518E1641CCAA19200ADFE75 /* Blog+Capabilities.swift */; }; - FABB259E2602FC2C00C8785C /* RestorePostTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 575E126E229779E70041B3EB /* RestorePostTableViewCell.swift */; }; FABB259F2602FC2C00C8785C /* MenuItemCategoriesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08216FAF1CDBF96000304BA7 /* MenuItemCategoriesViewController.m */; }; FABB25A02602FC2C00C8785C /* UINavigationBar+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171CC15724FCEBF7008B7180 /* UINavigationBar+Appearance.swift */; }; FABB25A12602FC2C00C8785C /* QuickStartChecklistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C9908D21067E22009EFFEB /* QuickStartChecklistViewController.swift */; }; @@ -6901,7 +6893,6 @@ 57569CF1230485680052EE14 /* PostAutoUploadInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PostAutoUploadInteractorTests.swift; path = Services/PostAutoUploadInteractorTests.swift; sourceTree = ""; }; 575802122357C41200E4C63C /* MediaCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MediaCoordinatorTests.swift; path = Services/MediaCoordinatorTests.swift; sourceTree = ""; }; 575E126222973EBB0041B3EB /* PostCompactCellGhostableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCompactCellGhostableTests.swift; sourceTree = ""; }; - 575E126E229779E70041B3EB /* RestorePostTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorePostTableViewCell.swift; sourceTree = ""; }; 577C2AAA22936DCB00AD1F03 /* PostCardCellGhostableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCardCellGhostableTests.swift; sourceTree = ""; }; 577C2AB322943FEC00AD1F03 /* PostCompactCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCompactCell.swift; sourceTree = ""; }; 577C2AB52294401800AD1F03 /* PostCompactCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PostCompactCell.xib; sourceTree = ""; }; @@ -6962,14 +6953,10 @@ 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageListSectionHeaderView.xib; sourceTree = ""; }; 5D146EB9189857ED0068FDC6 /* FeaturedImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeaturedImageViewController.h; sourceTree = ""; usesTabs = 0; }; 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeaturedImageViewController.m; sourceTree = ""; usesTabs = 0; }; - 5D18FE9C1AFBB17400EFEED0 /* RestorePageTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestorePageTableViewCell.h; sourceTree = ""; }; - 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestorePageTableViewCell.m; sourceTree = ""; }; - 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RestorePageTableViewCell.xib; sourceTree = ""; }; 5D1D04731B7A50B100CDE646 /* Reader.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Reader.storyboard; sourceTree = ""; }; 5D1D04741B7A50B100CDE646 /* ReaderStreamViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderStreamViewController.swift; sourceTree = ""; }; 5D229A78199AB74F00685123 /* WordPress 21.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 21.xcdatamodel"; sourceTree = ""; }; 5D2B30B81B7411C700DA15F3 /* ReaderCardDiscoverAttributionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderCardDiscoverAttributionView.swift; sourceTree = ""; }; - 5D2FB2821AE98C4600F1D4ED /* RestorePostTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RestorePostTableViewCell.xib; sourceTree = ""; }; 5D3D559518F88C3500782892 /* ReaderPostService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderPostService.h; sourceTree = ""; }; 5D3D559618F88C3500782892 /* ReaderPostService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderPostService.m; sourceTree = ""; }; 5D3E334C15EEBB6B005FC6F2 /* ReachabilityUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReachabilityUtils.h; sourceTree = ""; }; @@ -12526,8 +12513,6 @@ children = ( 597421B01CEB6874005D5F38 /* ConfigurablePostView.h */, 57D6C83F229498C4003DDC7E /* InteractivePostView.swift */, - 575E126E229779E70041B3EB /* RestorePostTableViewCell.swift */, - 5D2FB2821AE98C4600F1D4ED /* RestorePostTableViewCell.xib */, 5D732F951AE84E3C00CD89E7 /* PostListFooterView.h */, 5D732F961AE84E3C00CD89E7 /* PostListFooterView.m */, 5D732F981AE84E5400CD89E7 /* PostListFooterView.xib */, @@ -12583,9 +12568,6 @@ 5DFA7EC41AF814E40072023B /* PageListTableViewCell.h */, 5DFA7EC51AF814E40072023B /* PageListTableViewCell.m */, 5DFA7EC61AF814E40072023B /* PageListTableViewCell.xib */, - 5D18FE9C1AFBB17400EFEED0 /* RestorePageTableViewCell.h */, - 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */, - 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */, 834A49D12A0C23A90042ED3D /* TemplatePageTableViewCell.swift */, ); name = Views; @@ -19287,7 +19269,6 @@ 8BE6F92A27EE26D30008BDC7 /* BlogDashboardPostCardGhostCell.xib in Resources */, CE1CCB2F2050502B000EE3AC /* MyProfileHeaderView.xib in Resources */, 98BC522D27F62B6300D6E8C2 /* BloggingPromptsFeatureDescriptionView.xib in Resources */, - 5D18FEA01AFBB17400EFEED0 /* RestorePageTableViewCell.xib in Resources */, 17222DAB261DDDF90047B163 /* blue-icon-app-60x60@3x.png in Resources */, 747D09862034837C0085EABF /* WordPressShare.js in Resources */, 9A76C32F22AFDA2100F5D819 /* world-map.svg in Resources */, @@ -19311,7 +19292,6 @@ E1B912811BB00EFD003C25B9 /* People.storyboard in Resources */, 9826AE8321B5C6A700C851FA /* LatestPostSummaryCell.xib in Resources */, 8BF281F927CE8E4100AF8CF3 /* DashboardGhostCardContent.xib in Resources */, - 5D2FB2831AE98C4600F1D4ED /* RestorePostTableViewCell.xib in Resources */, 1724DDCC1C6121D00099D273 /* Plans.storyboard in Resources */, 40A2778120191B5E00D078D5 /* PluginDirectoryCollectionViewCell.xib in Resources */, 8BD8201924BCCE8600FF25FD /* ReaderWelcomeBanner.xib in Resources */, @@ -19810,7 +19790,6 @@ FABB20702602FC2C00C8785C /* ReaderListStreamHeader.xib in Resources */, F465979B28E65FC800D5F49A /* dark-green-icon-app-76@2x.png in Resources */, FABB20722602FC2C00C8785C /* MyProfileHeaderView.xib in Resources */, - FABB20742602FC2C00C8785C /* RestorePageTableViewCell.xib in Resources */, FABB20762602FC2C00C8785C /* WordPressShare.js in Resources */, C7234A512832C47D0045C63F /* QRLoginVerifyAuthorizationViewController.xib in Resources */, FE3E427926A868EC00C596CE /* ListTableHeaderView.xib in Resources */, @@ -19843,7 +19822,6 @@ FABB208B2602FC2C00C8785C /* LatestPostSummaryCell.xib in Resources */, F41E4ECF28F23E00001880C6 /* green-on-white-icon-app-83.5@2x.png in Resources */, F46597C728E668B900D5F49A /* neumorphic-light-icon-app-76@2x.png in Resources */, - FABB208D2602FC2C00C8785C /* RestorePostTableViewCell.xib in Resources */, FABB208E2602FC2C00C8785C /* Plans.storyboard in Resources */, FABB208F2602FC2C00C8785C /* PluginDirectoryCollectionViewCell.xib in Resources */, F41E4ECE28F23E00001880C6 /* green-on-white-icon-app-76@2x.png in Resources */, @@ -22433,7 +22411,6 @@ 0C391E5E2A2FE5350040EA91 /* DashboardBlazeCampaignView.swift in Sources */, 9881296E219CF1310075FF33 /* StatsCellHeader.swift in Sources */, E1823E6C1E42231C00C19F53 /* UIEdgeInsets.swift in Sources */, - 5D18FE9F1AFBB17400EFEED0 /* RestorePageTableViewCell.m in Sources */, F42A1D9729928B360059CC70 /* BlockedAuthor.swift in Sources */, 069A4AA62664448F00413FA9 /* GutenbergFeaturedImageHelper.swift in Sources */, B54C02241F38F50100574572 /* String+RegEx.swift in Sources */, @@ -22610,7 +22587,6 @@ FA8E1F7725EEFA7300063673 /* ReaderPostService+RelatedPosts.swift in Sources */, B518E1651CCAA19200ADFE75 /* Blog+Capabilities.swift in Sources */, FADC40AE2A8D2E8D00C19997 /* ImageDownloader+Gravatar.swift in Sources */, - 575E126F229779E70041B3EB /* RestorePostTableViewCell.swift in Sources */, 08216FC91CDBF96000304BA7 /* MenuItemCategoriesViewController.m in Sources */, 171CC15824FCEBF7008B7180 /* UINavigationBar+Appearance.swift in Sources */, 43C9908E21067E22009EFFEB /* QuickStartChecklistViewController.swift in Sources */, @@ -25260,7 +25236,6 @@ FABB25232602FC2C00C8785C /* StatsCellHeader.swift in Sources */, DCCDF75C283BEFEA00AA347E /* SiteStatsInsightsDetailsTableViewController.swift in Sources */, FABB25242602FC2C00C8785C /* UIEdgeInsets.swift in Sources */, - FABB25252602FC2C00C8785C /* RestorePageTableViewCell.m in Sources */, C7234A3B2832BA240045C63F /* QRLoginCoordinator.swift in Sources */, FABB25262602FC2C00C8785C /* String+RegEx.swift in Sources */, 175CC17A27230DC900622FB4 /* Bool+StringRepresentation.swift in Sources */, @@ -25431,7 +25406,6 @@ FABB259A2602FC2C00C8785C /* ReaderBlockSiteAction.swift in Sources */, FABB259C2602FC2C00C8785C /* ReaderPostService+RelatedPosts.swift in Sources */, FABB259D2602FC2C00C8785C /* Blog+Capabilities.swift in Sources */, - FABB259E2602FC2C00C8785C /* RestorePostTableViewCell.swift in Sources */, 1756F1E02822BB6F00CD0915 /* SparklineView.swift in Sources */, FABB259F2602FC2C00C8785C /* MenuItemCategoriesViewController.m in Sources */, FABB25A02602FC2C00C8785C /* UINavigationBar+Appearance.swift in Sources */, diff --git a/WordPress/WordPressTest/PostActionSheetTests.swift b/WordPress/WordPressTest/PostActionSheetTests.swift index 387c7a87c0de..a9ad83058c52 100644 --- a/WordPress/WordPressTest/PostActionSheetTests.swift +++ b/WordPress/WordPressTest/PostActionSheetTests.swift @@ -249,10 +249,6 @@ class InteractivePostViewDelegateMock: InteractivePostViewDelegate { } - func restore(_ post: AbstractPost) { - - } - func retry(_ post: AbstractPost) { didCallRetry = true } diff --git a/WordPress/WordPressTest/PostRepositoryTests.swift b/WordPress/WordPressTest/PostRepositoryTests.swift index d5c020b324e4..765a99acf347 100644 --- a/WordPress/WordPressTest/PostRepositoryTests.swift +++ b/WordPress/WordPressTest/PostRepositoryTests.swift @@ -54,7 +54,7 @@ class PostRepositoryTests: CoreDataTestCase { func testDeletePost() async throws { let postID = try await contextManager.performAndSave { context in - let post = PostBuilder(context).withRemote().with(title: "Post: Test").build() + let post = PostBuilder(context).with(status: .trash).withRemote().with(title: "Post: Test").build() return TaggedManagedObjectID(post) } @@ -69,7 +69,7 @@ class PostRepositoryTests: CoreDataTestCase { func testDeletePostWithRemoteFailure() async throws { let postID = try await contextManager.performAndSave { context in - let post = PostBuilder(context).withRemote().with(title: "Post: Test").build() + let post = PostBuilder(context).with(status: .trash).withRemote().with(title: "Post: Test").build() return TaggedManagedObjectID(post) } @@ -89,7 +89,7 @@ class PostRepositoryTests: CoreDataTestCase { func testDeleteHistory() async throws { let (firstRevision, secondRevision) = try await contextManager.performAndSave { context in - let first = PostBuilder(context).withRemote().with(title: "Post: Test").build() + let first = PostBuilder(context).with(status: .trash).withRemote().with(title: "Post: Test").build() let second = first.createRevision() second.postTitle = "Edited" return (TaggedManagedObjectID(first), TaggedManagedObjectID(second)) @@ -107,7 +107,7 @@ class PostRepositoryTests: CoreDataTestCase { func testDeleteLatest() async throws { let (firstRevision, secondRevision) = try await contextManager.performAndSave { context in - let first = PostBuilder(context).withRemote().with(title: "Post: Test").build() + let first = PostBuilder(context).with(status: .trash).withRemote().with(title: "Post: Test").build() let second = first.createRevision() second.postTitle = "Edited" return (TaggedManagedObjectID(first), TaggedManagedObjectID(second)) @@ -174,6 +174,33 @@ class PostRepositoryTests: CoreDataTestCase { XCTAssertTrue(isPostDeleted) } + func testTrashingAPostWillUpdateItsRevisionStatusAfterSyncProperty() async throws { + // Arrange + let (postID, revisionID) = try await contextManager.performAndSave { context in + let post = PostBuilder(context).with(statusAfterSync: .publish).withRemote().build() + let revision = post.createRevision() + return (TaggedManagedObjectID(post), TaggedManagedObjectID(revision)) + } + + let remotePost = RemotePost(siteID: 1, status: "trash", title: "Post: Test", content: "New content")! + remotePost.type = "post" + remoteMock.trashPostResult = .success(remotePost) + + // Act + try await repository.trash(postID) + + // Assert + let postStatusAfterSync = try await contextManager.performQuery { try $0.existingObject(with: postID).statusAfterSync } + let postStatus = try await contextManager.performQuery { try $0.existingObject(with: postID).status } + let revisionStatusAfterSync = try await contextManager.performQuery { try $0.existingObject(with: revisionID).statusAfterSync } + let revisionStatus = try await contextManager.performQuery { try $0.existingObject(with: revisionID).status } + + XCTAssertEqual(postStatusAfterSync, .trash) + XCTAssertEqual(postStatus, .trash) + XCTAssertEqual(revisionStatusAfterSync, .trash) + XCTAssertEqual(revisionStatus, .trash) + } + func testRestorePost() async throws { let postID = try await contextManager.performAndSave { context in let post = PostBuilder(context).withRemote().with(status: .trash).with(title: "Post: Test").build() diff --git a/WordPress/WordPressTest/Services/PostServiceWPComTests.swift b/WordPress/WordPressTest/Services/PostServiceWPComTests.swift index 26e7a15dbe06..04bdfe1c5c64 100644 --- a/WordPress/WordPressTest/Services/PostServiceWPComTests.swift +++ b/WordPress/WordPressTest/Services/PostServiceWPComTests.swift @@ -107,29 +107,6 @@ class PostServiceWPComTests: CoreDataTestCase { XCTAssertEqual(postFromDB.status, .draft) } - func testTrashingAPostWillUpdateItsRevisionStatusAfterSyncProperty() { - // Arrange - let post = PostBuilder(mainContext).with(statusAfterSync: .publish).withRemote().build() - let revision = post.createRevision() - try! mainContext.save() - - let remotePost = createRemotePost(.trash) - remoteMock.remotePostToReturnOnTrashPost = remotePost - let expectation = XCTestExpectation() - - // Act - self.service.trashPost(post, success: { - expectation.fulfill() - }, failure: self.impossibleFailureBlock) - wait(for: [expectation], timeout: timeout) - - // Assert - XCTAssertEqual(post.statusAfterSync, .trash) - XCTAssertEqual(post.status, .trash) - XCTAssertEqual(revision.statusAfterSync, .trash) - XCTAssertEqual(revision.status, .trash) - } - func testAutoSavingALocalDraftWillCallTheCreateEndpointInstead() { // Arrange let post = PostBuilder(mainContext).drafted().with(remoteStatus: .local).build()