From 02a2294330d266d8f6acca3057d5f4f1af55c292 Mon Sep 17 00:00:00 2001 From: whattherestimefor Date: Fri, 11 Apr 2025 13:54:34 -0400 Subject: [PATCH] Refetch status on reblog or favorite This is a temporary fix for favorites and boosts of boosted posts not displaying properly. A datamodel change in the future should make all of this logic less confusing. Fixes IOS-382 --- .../Provider/DataSourceFacade+Bookmark.swift | 2 +- .../Provider/DataSourceFacade+Favorite.swift | 16 +- .../Provider/DataSourceFacade+Reblog.swift | 17 +- .../Provider/DataSourceFacade+Status.swift | 20 ++- ...er+NotificationTableViewCellDelegate.swift | 2 +- ...Provider+StatusTableViewCellDelegate.swift | 2 +- ...tatusTableViewControllerNavigateable.swift | 10 +- .../Provider/DataSourceProvider.swift | 2 +- ...stsViewController+DataSourceProvider.swift | 4 +- ...ineViewController+DataSourceProvider.swift | 4 +- ...ineViewController+DataSourceProvider.swift | 4 +- ...ineViewController+DataSourceProvider.swift | 6 +- ...arkViewController+DataSourceProvider.swift | 4 +- .../FamiliarFollowersViewController.swift | 2 +- ...iteViewController+DataSourceProvider.swift | 4 +- .../Follower/FollowerListViewController.swift | 2 +- .../FollowingListViewController.swift | 2 +- .../Scene/Profile/ProfileViewController.swift | 10 +- ...ineViewController+DataSourceProvider.swift | 4 +- ...dByViewController+DataSourceProvider.swift | 2 +- ...dByViewController+DataSourceProvider.swift | 2 +- ...oryViewController+DataSourceProvider.swift | 2 +- ...ultViewController+DataSourceProvider.swift | 4 +- ...eadViewController+DataSourceProvider.swift | 2 +- .../Scene/Thread/ThreadViewController.swift | 4 +- .../DataController/FeedDataController.swift | 166 +++++++++++------- 26 files changed, 171 insertions(+), 128 deletions(-) diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift index 2cdf52108..3f525e75f 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift @@ -28,6 +28,6 @@ extension DataSourceFacade { newStatus.showDespiteContentWarning = status.showDespiteContentWarning newStatus.showDespiteFilter = status.showDespiteFilter - provider.update(status: newStatus, intent: .bookmark(updatedStatus.bookmarked == true)) + provider.update(contentStatus: newStatus, intent: .bookmark(updatedStatus.bookmarked == true)) } } diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift index 9595926b9..56537d027 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift @@ -14,19 +14,23 @@ extension DataSourceFacade { @MainActor public static func responseToStatusFavoriteAction( provider: DataSourceProvider & AuthContextProvider, - status: MastodonStatus + wrappingStatus: MastodonStatus, + contentStatus: MastodonStatus ) async throws { FeedbackGenerator.shared.generate(.selectionChanged) let updatedStatus = try await APIService.shared.favorite( - status: status, + status: contentStatus, authenticationBox: provider.authenticationBox ).value - let newStatus: MastodonStatus = .fromEntity(updatedStatus) - newStatus.showDespiteContentWarning = status.showDespiteContentWarning - newStatus.showDespiteFilter = status.showDespiteFilter + let showDespiteContentWarning = wrappingStatus.showDespiteContentWarning + let showDespiteFilter = wrappingStatus.showDespiteFilter - provider.update(status: newStatus, intent: .favorite(updatedStatus.favourited == true)) + let newStatus: MastodonStatus = .fromEntity(updatedStatus) + newStatus.showDespiteContentWarning = showDespiteContentWarning + newStatus.showDespiteFilter = showDespiteFilter + + provider.update(contentStatus: newStatus, intent: .favorite(updatedStatus.favourited == true)) } } diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift index 44a338cb3..098f632c0 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift @@ -15,28 +15,29 @@ extension DataSourceFacade { @MainActor static func responseToStatusReblogAction( provider: DataSourceProvider & AuthContextProvider, - status: MastodonStatus + wrappingStatus: MastodonStatus, + contentStatus: MastodonStatus ) async throws { if UserDefaults.shared.askBeforeBoostingAPost { let alertController = UIAlertController( - title: status.entity.reblogged == true ? L10n.Common.Alerts.BoostAPost.titleUnboost : L10n.Common.Alerts.BoostAPost.titleBoost, + title: contentStatus.entity.reblogged == true ? L10n.Common.Alerts.BoostAPost.titleUnboost : L10n.Common.Alerts.BoostAPost.titleBoost, message: nil, preferredStyle: .alert ) let cancelAction = UIAlertAction(title: L10n.Common.Alerts.BoostAPost.cancel, style: .default) alertController.addAction(cancelAction) let confirmAction = UIAlertAction( - title: status.entity.reblogged == true ? L10n.Common.Alerts.BoostAPost.unboost : L10n.Common.Alerts.BoostAPost.boost, + title: contentStatus.entity.reblogged == true ? L10n.Common.Alerts.BoostAPost.unboost : L10n.Common.Alerts.BoostAPost.boost, style: .default ) { _ in Task { @MainActor in - try? await performReblog(provider: provider, status: status) + try? await performReblog(provider: provider, status: contentStatus) } } alertController.addAction(confirmAction) provider.present(alertController, animated: true) } else { - try await performReblog(provider: provider, status: status) + try await performReblog(provider: provider, status: contentStatus) } } } @@ -49,17 +50,17 @@ private extension DataSourceFacade { ) async throws { FeedbackGenerator.shared.generate(.selectionChanged) - let updatedStatus = try await APIService.shared.reblog( + let updatedContentStatus = try await APIService.shared.reblog( status: status, authenticationBox: provider.authenticationBox ).value - let newStatus: MastodonStatus = .fromEntity(updatedStatus) + let newStatus: MastodonStatus = .fromEntity(updatedContentStatus) newStatus.reblog?.showDespiteContentWarning = status.showDespiteContentWarning newStatus.reblog?.showDespiteFilter = status.showDespiteFilter newStatus.showDespiteContentWarning = status.showDespiteContentWarning newStatus.showDespiteFilter = status.showDespiteFilter - provider.update(status: newStatus, intent: .reblog(updatedStatus.reblogged == true)) + provider.update(contentStatus: newStatus, intent: .reblog(updatedContentStatus.reblogged == true)) } } diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift index f55275f7d..73ac75ae8 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift @@ -28,7 +28,7 @@ extension DataSourceFacade { authenticationBox: dependency.authenticationBox ).value.asMastodonStatus - dependency.update(status: deletedStatus, intent: .delete) + dependency.update(contentStatus: deletedStatus, intent: .delete) } } @@ -116,12 +116,14 @@ extension DataSourceFacade { case .reblog: try await DataSourceFacade.responseToStatusReblogAction( provider: provider, - status: _status + wrappingStatus: status, + contentStatus: _status ) case .like: try await DataSourceFacade.responseToStatusFavoriteAction( provider: provider, - status: _status + wrappingStatus: status, + contentStatus: _status ) case .share: try await DataSourceFacade.responseToStatusShareAction( @@ -391,19 +393,19 @@ extension DataSourceFacade { alertController.addAction(cancelAction) dependency.present(alertController, animated: true) case .boostStatus(_): - guard let status: MastodonStatus = menuContext.statusViewModel?._originalStatus?.reblog ?? menuContext.statusViewModel?._originalStatus else { + guard let wrappingStatus = menuContext.statusViewModel?._originalStatus else { assertionFailure() return } - - try await responseToStatusReblogAction(provider: dependency, status: status) + let contentStatus = menuContext.statusViewModel?._originalStatus?.reblog ?? wrappingStatus + try await responseToStatusReblogAction(provider: dependency, wrappingStatus: wrappingStatus, contentStatus: contentStatus) case .favoriteStatus(_): - guard let status: MastodonStatus = menuContext.statusViewModel?._originalStatus?.reblog ?? menuContext.statusViewModel?._originalStatus else { + guard let wrappingStatus: MastodonStatus = menuContext.statusViewModel?._originalStatus else { assertionFailure() return } - - try await responseToStatusFavoriteAction(provider: dependency, status: status) + let contentStatus = menuContext.statusViewModel?._originalStatus?.reblog ?? wrappingStatus + try await responseToStatusFavoriteAction(provider: dependency, wrappingStatus: wrappingStatus, contentStatus: contentStatus) case .copyStatusLink: guard let status: MastodonStatus = menuContext.statusViewModel?._originalStatus?.reblog ?? menuContext.statusViewModel?._originalStatus else { assertionFailure() diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift index 7ed85e137..7a39f0089 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift @@ -585,7 +585,7 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut let newStatus: MastodonStatus = .fromEntity(entity) newStatus.poll = MastodonPoll(poll: newPoll, status: newStatus) - self.update(status: newStatus, intent: .pollVote) + self.update(contentStatus: newStatus, intent: .pollVote) } catch { notificationView.statusView.viewModel.isVoting = false } diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift index 432522c4b..5b4e2daca 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift @@ -320,7 +320,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte let newStatus: MastodonStatus = .fromEntity(entity) newStatus.poll = MastodonPoll(poll: newPoll, status: newStatus) - self.update(status: newStatus, intent: .pollVote) + self.update(contentStatus: newStatus, intent: .pollVote) } catch { let alert = UIAlertController(title: "Poll Error", message: "Something went wrong while processing your response: \(error)", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .cancel)) diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift index 60ed0e6e3..783fb7729 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift @@ -139,11 +139,12 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid @MainActor private func toggleReblog() async { guard let status = await statusRecord() else { return } - + let contentStatus = status.reblog ?? status do { try await DataSourceFacade.responseToStatusReblogAction( provider: self, - status: status + wrappingStatus: status, + contentStatus: contentStatus ) } catch { assertionFailure() @@ -153,11 +154,12 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid @MainActor private func toggleFavorite() async { guard let status = await statusRecord() else { return } - + let contentStatus = status.reblog ?? status do { try await DataSourceFacade.responseToStatusFavoriteAction( provider: self, - status: status + wrappingStatus: status, + contentStatus: contentStatus ) } catch { assertionFailure() diff --git a/Mastodon/Protocol/Provider/DataSourceProvider.swift b/Mastodon/Protocol/Provider/DataSourceProvider.swift index 10e33f6be..64b87b5a5 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider.swift @@ -38,7 +38,7 @@ extension DataSourceItem { protocol DataSourceProvider: UIViewController { func item(from source: DataSourceItem.Source) async -> DataSourceItem? - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) var filterContext: Mastodon.Entity.FilterContext? { get } func didToggleContentWarningDisplayStatus(status: MastodonStatus) diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController+DataSourceProvider.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController+DataSourceProvider.swift index 3a52ec810..5eaecc527 100644 --- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController+DataSourceProvider.swift @@ -36,8 +36,8 @@ extension DiscoveryPostsViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController+DataSourceProvider.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController+DataSourceProvider.swift index 8c9d3bc50..1f68bb27b 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController+DataSourceProvider.swift @@ -36,8 +36,8 @@ extension HashtagTimelineViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DataSourceProvider.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DataSourceProvider.swift index 1540a0284..89e6d0a84 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DataSourceProvider.swift @@ -39,8 +39,8 @@ extension HomeTimelineViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel?.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel?.dataController.update(status: contentStatus, intent: intent) } private func indexPath(for cell: UITableViewCell) -> IndexPath? { diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController+DataSourceProvider.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController+DataSourceProvider.swift index 1f056e3ca..c24687459 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController+DataSourceProvider.swift @@ -67,9 +67,9 @@ extension NotificationTimelineViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - MastodonFeedItemCacheManager.shared.addToCache(status.entity) - if let reblog = status.entity.reblog { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + MastodonFeedItemCacheManager.shared.addToCache(contentStatus.entity) + if let reblog = contentStatus.entity.reblog { MastodonFeedItemCacheManager.shared.addToCache(reblog) } viewModel.reloadData() diff --git a/Mastodon/Scene/Profile/Bookmark/BookmarkViewController+DataSourceProvider.swift b/Mastodon/Scene/Profile/Bookmark/BookmarkViewController+DataSourceProvider.swift index 50d6d1bc4..253075b49 100644 --- a/Mastodon/Scene/Profile/Bookmark/BookmarkViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Profile/Bookmark/BookmarkViewController+DataSourceProvider.swift @@ -36,8 +36,8 @@ extension BookmarkViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift index d72dcdc2f..3a2814a4d 100644 --- a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift +++ b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift @@ -106,7 +106,7 @@ extension FamiliarFollowersViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Profile/Favorite/FavoriteViewController+DataSourceProvider.swift b/Mastodon/Scene/Profile/Favorite/FavoriteViewController+DataSourceProvider.swift index 884bee4f0..2177add51 100644 --- a/Mastodon/Scene/Profile/Favorite/FavoriteViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Profile/Favorite/FavoriteViewController+DataSourceProvider.swift @@ -36,8 +36,8 @@ extension FavoriteViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift index 06e345cfe..1f8685866 100644 --- a/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift @@ -148,7 +148,7 @@ extension FollowerListViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewController.swift b/Mastodon/Scene/Profile/Following/FollowingListViewController.swift index c95a55d4d..5a878c13d 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewController.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewController.swift @@ -153,7 +153,7 @@ extension FollowingListViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 9546fb7b6..a975fd22e 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -1030,15 +1030,15 @@ extension ProfileViewController: DataSourceProvider { profilePagingViewController?.reloadTables() } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - updateViewModelsWithDataControllers(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + updateViewModelsWithDataControllers(status: contentStatus, intent: intent) } func updateViewModelsWithDataControllers(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - profilePagingViewController?.viewModel?.postUserTimelineViewController.update(status: status, intent: intent) - profilePagingViewController?.viewModel?.repliesUserTimelineViewController.update(status: status, intent: intent) - profilePagingViewController?.viewModel?.mediaUserTimelineViewController.update(status: status, intent: intent) + profilePagingViewController?.viewModel?.postUserTimelineViewController.update(contentStatus: status, intent: intent) + profilePagingViewController?.viewModel?.repliesUserTimelineViewController.update(contentStatus: status, intent: intent) + profilePagingViewController?.viewModel?.mediaUserTimelineViewController.update(contentStatus: status, intent: intent) } } diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController+DataSourceProvider.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController+DataSourceProvider.swift index c026dbe51..1d633c4a7 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController+DataSourceProvider.swift @@ -36,8 +36,8 @@ extension UserTimelineViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/Profile/UserList/FavoritedBy/FavoritedByViewController+DataSourceProvider.swift b/Mastodon/Scene/Profile/UserList/FavoritedBy/FavoritedByViewController+DataSourceProvider.swift index e4608f89b..684ad5b7f 100644 --- a/Mastodon/Scene/Profile/UserList/FavoritedBy/FavoritedByViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Profile/UserList/FavoritedBy/FavoritedByViewController+DataSourceProvider.swift @@ -36,7 +36,7 @@ extension FavoritedByViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Profile/UserList/RebloggedBy/RebloggedByViewController+DataSourceProvider.swift b/Mastodon/Scene/Profile/UserList/RebloggedBy/RebloggedByViewController+DataSourceProvider.swift index 737531001..837eeb301 100644 --- a/Mastodon/Scene/Profile/UserList/RebloggedBy/RebloggedByViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Profile/UserList/RebloggedBy/RebloggedByViewController+DataSourceProvider.swift @@ -37,7 +37,7 @@ extension RebloggedByViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController+DataSourceProvider.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController+DataSourceProvider.swift index 0600637fa..45e4641fb 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController+DataSourceProvider.swift @@ -37,7 +37,7 @@ extension SearchHistoryViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { assertionFailure("Not required") } diff --git a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController+DataSourceProvider.swift b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController+DataSourceProvider.swift index ff593c545..f7f12762b 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController+DataSourceProvider.swift @@ -41,8 +41,8 @@ extension SearchResultViewController: DataSourceProvider { } } - func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { - viewModel.dataController.update(status: status, intent: intent) + func update(contentStatus: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + viewModel.dataController.update(status: contentStatus, intent: intent) } @MainActor diff --git a/Mastodon/Scene/Thread/ThreadViewController+DataSourceProvider.swift b/Mastodon/Scene/Thread/ThreadViewController+DataSourceProvider.swift index 088e3c327..a3df20304 100644 --- a/Mastodon/Scene/Thread/ThreadViewController+DataSourceProvider.swift +++ b/Mastodon/Scene/Thread/ThreadViewController+DataSourceProvider.swift @@ -37,7 +37,7 @@ extension ThreadViewController: DataSourceProvider { } } - func update(status _status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { + func update(contentStatus _status: MastodonStatus, intent: MastodonStatus.UpdateIntent) { let status = _status.reblog ?? _status if case MastodonStatus.UpdateIntent.delete = intent { return handleDelete(status) diff --git a/Mastodon/Scene/Thread/ThreadViewController.swift b/Mastodon/Scene/Thread/ThreadViewController.swift index 6397c1cee..9ebd8cf51 100644 --- a/Mastodon/Scene/Thread/ThreadViewController.swift +++ b/Mastodon/Scene/Thread/ThreadViewController.swift @@ -197,13 +197,13 @@ extension ThreadViewController: StatusTableViewControllerNavigateable { extension UINavigationController { func notifyChildrenAboutStatusDeletion(_ status: MastodonStatus) { viewControllers.compactMap { $0 as? DataSourceProvider }.forEach { provider in - provider?.update(status: status, intent: .delete) + provider?.update(contentStatus: status, intent: .delete) } } func notifyChildrenAboutStatusEdit(_ status: MastodonStatus) { viewControllers.compactMap { $0 as? DataSourceProvider }.forEach { provider in - provider?.update(status: status, intent: .edit) + provider?.update(contentStatus: status, intent: .edit) } } } diff --git a/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift b/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift index 82c99f2ec..eff11e2b7 100644 --- a/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift +++ b/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift @@ -119,93 +119,127 @@ final public class FeedDataController { @MainActor private func updateBookmarked(_ status: MastodonStatus, _ isBookmarked: Bool) { var newRecords = Array(records) - guard let index = newRecords.firstIndex(where: { $0.id == status.id }) else { - logger.warning("\(Self.entryNotFoundMessage)") - return + + let relevant = recordsContaining(statusID: status.id) + Task { + let refetched = await refetchStatuses(relevant) + + for record in refetched { + if let idx = newRecords.firstIndex(where: { $0.id == record.id }) { + let existingRecord = newRecords[idx] + newRecords[idx] = .fromStatus(MastodonStatus(entity: record, showDespiteContentWarning: existingRecord.status?.showDespiteContentWarning ?? false), kind: existingRecord.kind) + } else { + logger.warning("\(Self.entryNotFoundMessage)") + return + } + } + records = newRecords } - let existingRecord = newRecords[index] - let newStatus = status.inheritSensitivityToggled(from: existingRecord.status) - newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) - records = newRecords } @MainActor private func updateFavorited(_ status: MastodonStatus, _ isFavorited: Bool) { var newRecords = Array(records) - if let index = newRecords.firstIndex(where: { $0.id == status.id }) { - // Replace old status entity - let existingRecord = newRecords[index] - let newStatus = status.inheritSensitivityToggled(from: existingRecord.status).withOriginal(status: existingRecord.status?.originalStatus) - newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) - } else if let index = newRecords.firstIndex(where: { $0.status?.reblog?.id == status.id }) { - // Replace reblogged entity of old "parent" status - let newStatus: MastodonStatus - if let existingEntity = newRecords[index].status?.entity { - newStatus = .fromEntity(existingEntity) - newStatus.originalStatus = newRecords[index].status?.originalStatus - newStatus.reblog = status - } else { - newStatus = status + let relevant = recordsContaining(statusID: status.id) + Task { + let refetched = await refetchStatuses(relevant) + + for record in refetched { + if let idx = newRecords.firstIndex(where: { $0.id == record.id }) { + let existingRecord = newRecords[idx] + newRecords[idx] = .fromStatus(MastodonStatus(entity: record, showDespiteContentWarning: existingRecord.status?.showDespiteContentWarning ?? false), kind: existingRecord.kind) + } else { + logger.warning("\(Self.entryNotFoundMessage)") + return + } } - newRecords[index] = .fromStatus(newStatus, kind: newRecords[index].kind) - } else { - logger.warning("\(Self.entryNotFoundMessage)") + records = newRecords } - records = newRecords } @MainActor private func updateReblogged(_ status: MastodonStatus, _ isReblogged: Bool) { var newRecords = Array(records) - - switch isReblogged { - case true: - let index: Int - if let idx = newRecords.firstIndex(where: { $0.status?.reblog?.id == status.reblog?.id }) { - index = idx - } else if let idx = newRecords.firstIndex(where: { $0.id == status.reblog?.id }) { - index = idx - } else { - logger.warning("\(Self.entryNotFoundMessage)") - return + let relevantID = isReblogged ? (status.reblog?.id ?? status.id) : status.id + let relevant = recordsContaining(statusID: relevantID) + Task { + let refetched = await refetchStatuses(relevant) +// print("found \(refetched.count) relevant statuses for \(status.id)") + + for record in refetched { + if let idx = newRecords.firstIndex(where: { $0.id == record.id }) { + let existingRecord = newRecords[idx] +// print("replacing record for \(existingRecord.status?.id) (reblog of \(existingRecord.status?.reblog))...") + if existingRecord.status?.entity.reblogged == true || existingRecord.status?.reblog?.entity.reblogged == true { +// print("- was reblogged by me") + } else { +// print("- NOT reblogged by me") + } + let newRecord = MastodonFeed.fromStatus(MastodonStatus(entity: record, showDespiteContentWarning: existingRecord.status?.showDespiteContentWarning ?? false), kind: existingRecord.kind) + newRecords[idx] = newRecord +// print("replaced with \(newRecord.status?.id) (reblog of \(newRecord.status?.reblog?.id))") + if newRecord.status?.entity.reblogged == true || newRecord.status?.reblog?.entity.reblogged == true { +// print("- was reblogged by me") + } else { +// print("- NOT reblogged by me") + } + } else { + logger.warning("\(Self.entryNotFoundMessage)") + return + } } - let existingRecord = newRecords[index] - newRecords[index] = .fromStatus(status.withOriginal(status: existingRecord.status), kind: existingRecord.kind) - case false: - let index: Int - if let idx = newRecords.firstIndex(where: { $0.status?.reblog?.id == status.id }) { - index = idx - } else if let idx = newRecords.firstIndex(where: { $0.status?.id == status.id }) { - index = idx - } else { - logger.warning("\(Self.entryNotFoundMessage)") - return + records = newRecords + } + } + + @MainActor + private func updateSensitive(_ status: MastodonStatus, _ isVisible: Bool) { + let toUpdate = recordsContaining(statusID: status.id) + var newRecords = Array(records) + for record in toUpdate { + if let index = newRecords.firstIndex(where: { $0.status?.id == record.id }), let existingEntity = newRecords[index].status?.entity { + if existingEntity.id == status.id { + let existingRecord = newRecords[index] + let newStatus: MastodonStatus = .fromEntity(existingEntity) + newStatus.reblog = status + newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) + } else if existingEntity.reblog?.id == status.id { + let existingRecord = newRecords[index] + let newStatus: MastodonStatus = .fromEntity(existingEntity) + .inheritSensitivityToggled(from: status) + newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) + } else { + logger.warning("\(Self.entryNotFoundMessage)") + return + } } - let existingRecord = newRecords[index] - let newStatus = existingRecord.status?.originalStatus ?? status.inheritSensitivityToggled(from: existingRecord.status) - newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) } records = newRecords } @MainActor - private func updateSensitive(_ status: MastodonStatus, _ isVisible: Bool) { - var newRecords = Array(records) - if let index = newRecords.firstIndex(where: { $0.status?.reblog?.id == status.id }), let existingEntity = newRecords[index].status?.entity { - let existingRecord = newRecords[index] - let newStatus: MastodonStatus = .fromEntity(existingEntity) - newStatus.reblog = status - newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) - } else if let index = newRecords.firstIndex(where: { $0.id == status.id }), let existingEntity = newRecords[index].status?.entity { - let existingRecord = newRecords[index] - let newStatus: MastodonStatus = .fromEntity(existingEntity) - .inheritSensitivityToggled(from: status) - newRecords[index] = .fromStatus(newStatus, kind: existingRecord.kind) - } else { - logger.warning("\(Self.entryNotFoundMessage)") - return + private func recordsContaining(statusID: Mastodon.Entity.Status.ID) -> [MastodonFeed] { + records.filter { feed in + return feed.status?.id == statusID || feed.status?.reblog?.id == statusID + } + } + + @MainActor + private func refetchStatuses(_ items: [MastodonFeed]) async -> [Mastodon.Entity.Status] { + + switch kind { + case .notificationAll, .notificationMentions, .notificationAccount: + return [] + default: + var refetched = [Mastodon.Entity.Status]() + for item in items { + if let refetchedItem = try? await APIService.shared.status(statusID: item.id, authenticationBox: authenticationBox) { + refetched.append(refetchedItem.value) + } else { + } + } + return refetched } - records = newRecords } }