diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index 66658da3..83cb107c 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -104,12 +104,27 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie switch state { case .unloaded: Task { - await load() + await self.load() } case .loaded, .setupInitialSnapshot: - var snapshot = dataSource.snapshot() + var snapshot = self.dataSource.snapshot() snapshot.reconfigureItems([.header(id)]) - dataSource.apply(snapshot, animatingDifferences: true) + self.dataSource.apply(snapshot, animatingDifferences: true) + } + } + .store(in: &cancellables) + + mastodonController.persistentContainer.relationshipSubject + .receive(on: DispatchQueue.main) + .filter { [unowned self] in $0 == self.accountID } + .sink { [unowned self] id in + switch state { + case .unloaded: + break + case .loaded, .setupInitialSnapshot: + var snapshot = self.dataSource.snapshot() + snapshot.reconfigureItems([.header(id)]) + self.dataSource.apply(snapshot, animatingDifferences: true) } } .store(in: &cancellables) @@ -132,6 +147,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie switch itemIdentifier { case .header(let id): if let headerCell = self.headerCell { + headerCell.view?.updateUI(for: id) return headerCell } else { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! ProfileHeaderCollectionViewCell @@ -145,6 +161,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie view.pagesSegmentedControl.setSelectedOption(self.owner!.currentPage, animated: false) cell.addHeader(view) case .useExistingView(let view): + view.updateUI(for: id) cell.addHeader(view) case .placeholder(height: let height): _ = cell.addConstraint(height: height) @@ -205,6 +222,13 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie state = .setupInitialSnapshot + Task { + if let (all, _) = try? await mastodonController.run(Client.getRelationships(accounts: [accountID])), + let relationship = all.first { + self.mastodonController.persistentContainer.addOrUpdate(relationship: relationship) + } + } + await controller.loadInitial() await tryLoadPinned() diff --git a/Tusker/Screens/Utilities/Previewing.swift b/Tusker/Screens/Utilities/Previewing.swift index 6c31fbd3..3ba68f32 100644 --- a/Tusker/Screens/Utilities/Previewing.swift +++ b/Tusker/Screens/Utilities/Previewing.swift @@ -39,7 +39,7 @@ extension MenuActionProvider { private var mastodonController: MastodonController? { navigationDelegate?.apiController } - func actionsForProfile(accountID: String, source: PopoverSource) -> [UIMenuElement] { + func actionsForProfile(accountID: String, source: PopoverSource, fetchRelationship: Bool = true) -> [UIMenuElement] { guard let mastodonController = mastodonController, let account = mastodonController.persistentContainer.account(for: accountID) else { return [] } @@ -68,7 +68,7 @@ extension MenuActionProvider { if let ownAccount = mastodonController.account, accountID != ownAccount.id { - actionsSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) })) + actionsSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) })) actionsSection.append(UIDeferredMenuElement.uncached({ elementHandler in let listActions = mastodonController.lists.map { list in UIAction(title: list.title, image: UIImage(systemName: "plus")) { [unowned self] _ in @@ -82,8 +82,8 @@ extension MenuActionProvider { } elementHandler([UIMenu(title: "Add to List", image: UIImage(systemName: "list.bullet"), children: listActions)]) })) - suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) })) - suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.muteAction(for: $0, mastodonController: $1) })) + suppressSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) })) + suppressSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.muteAction(for: $0, mastodonController: $1) })) } addOpenInNewWindow(actions: &shareSection, activity: UserActivityManager.showProfileActivity(id: accountID, accountID: loggedInAccountID)) @@ -377,16 +377,16 @@ extension MenuActionProvider { } } - private func relationshipAction(accountID: String, mastodonController: MastodonController, builder: @escaping @MainActor (RelationshipMO, MastodonController) -> UIMenuElement) -> UIDeferredMenuElement { + private func relationshipAction(_ fetch: Bool, accountID: String, mastodonController: MastodonController, builder: @escaping @MainActor (RelationshipMO, MastodonController) -> UIMenuElement) -> UIDeferredMenuElement { return UIDeferredMenuElement.uncached({ @MainActor elementHandler in - let relationship = Task { - await fetchRelationship(accountID: accountID, mastodonController: mastodonController) - } // workaround for #198, may result in showing outdated relationship, so only do so where necessary - if ProcessInfo.processInfo.isiOSAppOnMac, + if !fetch || ProcessInfo.processInfo.isiOSAppOnMac, let mo = mastodonController.persistentContainer.relationship(forAccount: accountID) { elementHandler([builder(mo, mastodonController)]) } else { + let relationship = Task { + await fetchRelationship(accountID: accountID, mastodonController: mastodonController) + } Task { @MainActor in if let relationship = await relationship.value { elementHandler([builder(relationship, mastodonController)]) diff --git a/Tusker/Views/Profile Header/ProfileHeaderView.swift b/Tusker/Views/Profile Header/ProfileHeaderView.swift index 1cb33ece..244f66dc 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderView.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderView.swift @@ -8,7 +8,6 @@ import UIKit import Pachyderm -import Combine protocol ProfileHeaderViewDelegate: TuskerNavigationDelegate, MenuActionProvider { func profileHeader(_ headerView: ProfileHeaderView, selectedPageChangedTo newPage: ProfileViewController.Page) @@ -21,11 +20,7 @@ class ProfileHeaderView: UIView { return nib.instantiate(withOwner: nil, options: nil).first as! ProfileHeaderView } - weak var delegate: ProfileHeaderViewDelegate? { - didSet { - createObservers() - } - } + weak var delegate: ProfileHeaderViewDelegate? var mastodonController: MastodonController! { delegate?.apiController } @IBOutlet weak var headerImageView: UIImageView! @@ -57,8 +52,6 @@ class ProfileHeaderView: UIView { } } - private var cancellables = [AnyCancellable]() - deinit { avatarRequest?.cancel() headerRequest?.cancel() @@ -122,27 +115,6 @@ class ProfileHeaderView: UIView { NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) } - private func createObservers() { - // mastodonController may be nil if the ProfileViewController is deinit'd before the header is even created - guard let mastodonController else { - return - } - - cancellables = [] - - mastodonController.persistentContainer.accountSubject - .receive(on: DispatchQueue.main) - .filter { [weak self] in $0 == self?.accountID } - .sink { [weak self] in self?.updateUI(for: $0) } - .store(in: &cancellables) - - mastodonController.persistentContainer.relationshipSubject - .receive(on: DispatchQueue.main) - .filter { [weak self] in $0 == self?.accountID } - .sink { [weak self] (_) in self?.updateRelationship() } - .store(in: &cancellables) - } - func updateUI(for accountID: String) { self.accountID = accountID @@ -158,7 +130,7 @@ class ProfileHeaderView: UIView { updateImages(account: account) - moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForProfile(accountID: accountID, source: .view(moreButton)) ?? []) + moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForProfile(accountID: accountID, source: .view(moreButton), fetchRelationship: false) ?? []) noteTextView.navigationDelegate = delegate noteTextView.setTextFromHtml(account.note) @@ -171,16 +143,6 @@ class ProfileHeaderView: UIView { // while fetching the most up-to-date, show the current data (if any) updateRelationship() - - let request = Client.getRelationships(accounts: [accountID]) - mastodonController.run(request) { [weak self] (response) in - guard let mastodonController = self?.mastodonController, - case let .success(results, _) = response, - let relationship = results.first else { - return - } - mastodonController.persistentContainer.addOrUpdate(relationship: relationship) - } } fieldsView.delegate = delegate