From 478ba3db286493d013be00530050dcd2a6829578 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 12 Dec 2022 22:02:07 -0500 Subject: [PATCH] Include followed hashtags in Explore and sidebar --- .../Explore/ExploreViewController.swift | 48 ++++++++++++------- .../Main/MainSidebarViewController.swift | 43 ++++++++++------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index 160f3c8f..82e3323d 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -24,7 +24,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { var searchControllerStatusOnAppearance: Bool? = nil - private var listsCancellable: AnyCancellable? + private var cancellables = Set() init(mastodonController: MastodonController) { self.mastodonController = mastodonController @@ -70,12 +70,26 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { navigationItem.searchController = searchController navigationItem.hidesSearchBarWhenScrolling = false - NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) - listsCancellable = mastodonController.$lists + mastodonController.$lists .sink { [unowned self] in self.reloadLists($0) } + .store(in: &cancellables) + mastodonController.$followedHashtags + .merge(with: + NotificationCenter.default.publisher(for: .savedHashtagsChanged) + .map { [unowned self] _ in self.mastodonController.followedHashtags } + ) + .sink { [unowned self] in self.updateHashtagsSection(followed: $0) } + .store(in: &cancellables) + + let a = PassthroughSubject() + let b = PassthroughSubject() + + a.merge(with: b) + .sink(receiveValue: { print($0) }) + .store(in: &cancellables) } override func viewWillAppear(_ animated: Bool) { @@ -149,9 +163,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { addDiscoverSection(to: &snapshot) } snapshot.appendItems([.addList], toSection: .lists) - let hashtags = fetchSavedHashtags().map { - Item.savedHashtag(Hashtag(name: $0.name, url: $0.url)) - } + let hashtags = fetchHashtagItems(followed: mastodonController.followedHashtags) snapshot.appendItems(hashtags, toSection: .savedHashtags) snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags) let instances = fetchSavedInstances().map { @@ -193,14 +205,16 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { } @MainActor - private func fetchSavedHashtags() -> [SavedHashtag] { - let req = SavedHashtag.fetchRequest() - req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.localizedCompare(_:)))] - do { - return try mastodonController.persistentContainer.viewContext.fetch(req) - } catch { - return [] + private func fetchHashtagItems(followed: [FollowedHashtag]) -> [Item] { + let saved = (try? mastodonController.persistentContainer.viewContext.fetch(SavedHashtag.fetchRequest())) ?? [] + var items = saved.map { + Item.savedHashtag(Hashtag(name: $0.name, url: $0.url)) } + for followed in followed where !saved.contains(where: { $0.name == followed.name }) { + items.append(.savedHashtag(Hashtag(name: followed.name, url: followed.url))) + } + items.sort(using: SemiCaseSensitiveComparator.keyPath(\.label)) + return items } @MainActor @@ -214,12 +228,10 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { } } - @objc private func savedHashtagsChanged() { + private func updateHashtagsSection(followed: [FollowedHashtag]) { var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags)) - let hashtags = fetchSavedHashtags().map { - Item.savedHashtag(Hashtag(name: $0.name, url: $0.url)) - } + let hashtags = fetchHashtagItems(followed: followed) snapshot.appendItems(hashtags, toSection: .savedHashtags) snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags) dataSource.apply(snapshot) @@ -386,7 +398,7 @@ extension ExploreViewController { case .lists: return NSLocalizedString("Lists", comment: "explore lists section title") case .savedHashtags: - return NSLocalizedString("Saved Hashtags", comment: "explore saved hashtags section title") + return NSLocalizedString("Hashtags", comment: "explore saved hashtags section title") case .savedInstances: return NSLocalizedString("Instance Timelines", comment: "explore instance timelines section title") } diff --git a/Tusker/Screens/Main/MainSidebarViewController.swift b/Tusker/Screens/Main/MainSidebarViewController.swift index 4101d260..07d2d7d1 100644 --- a/Tusker/Screens/Main/MainSidebarViewController.swift +++ b/Tusker/Screens/Main/MainSidebarViewController.swift @@ -30,7 +30,7 @@ class MainSidebarViewController: UIViewController { private var collectionView: UICollectionView! private var dataSource: UICollectionViewDiffableDataSource! - private var listsCancellable: AnyCancellable? + private var cancellables = Set() var allItems: [Item] { [ @@ -101,12 +101,19 @@ class MainSidebarViewController: UIViewController { select(item: .tab(.timelines), animated: false) - NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedHashtags), name: .savedHashtagsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedInstances), name: .savedInstancesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) - listsCancellable = mastodonController.$lists + mastodonController.$lists .sink { [unowned self] in self.reloadLists($0) } + .store(in: &cancellables) + mastodonController.$followedHashtags + .merge(with: + NotificationCenter.default.publisher(for: .savedHashtagsChanged) + .map { [unowned self] _ in self.mastodonController.followedHashtags } + ) + .sink { [unowned self] in self.updateHashtagsSection(followed: $0) } + .store(in: &cancellables) onViewDidLoad?() } @@ -176,7 +183,7 @@ class MainSidebarViewController: UIViewController { applyDiscoverSectionSnapshot() reloadLists(mastodonController.lists) - reloadSavedHashtags() + updateHashtagsSection(followed: mastodonController.followedHashtags) reloadSavedInstances() } @@ -224,14 +231,16 @@ class MainSidebarViewController: UIViewController { } @MainActor - private func fetchSavedHashtags() -> [SavedHashtag] { - let req = SavedHashtag.fetchRequest() - req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.localizedCompare(_:)))] - do { - return try mastodonController.persistentContainer.viewContext.fetch(req) - } catch { - return [] + private func fetchHashtagItems(followed: [FollowedHashtag]) -> [Item] { + let saved = (try? mastodonController.persistentContainer.viewContext.fetch(SavedHashtag.fetchRequest())) ?? [] + var items = saved.map { + Item.savedHashtag(Hashtag(name: $0.name, url: $0.url)) } + for followed in followed where !saved.contains(where: { $0.name == followed.name }) { + items.append(.savedHashtag(Hashtag(name: followed.name, url: followed.url))) + } + items.sort(using: SemiCaseSensitiveComparator.keyPath(\.title)) + return items } @MainActor @@ -245,10 +254,8 @@ class MainSidebarViewController: UIViewController { } } - @objc private func reloadSavedHashtags() { - let hashtags = fetchSavedHashtags().map { - Item.savedHashtag(Hashtag(name: $0.name, url: $0.url)) - } + private func updateHashtagsSection(followed: [FollowedHashtag]) { + let hashtags = fetchHashtagItems(followed: followed) if let selectedItem, case .savedHashtag(_) = selectedItem, !hashtags.contains(selectedItem) { @@ -403,13 +410,13 @@ extension MainSidebarViewController { case .addList: return "New List..." case .savedHashtagsHeader: - return "Saved Hashtags" + return "Hashtags" case let .savedHashtag(hashtag): return hashtag.name case .addSavedHashtag: - return "Save Hashtag..." + return "Add Hashtag..." case .savedInstancesHeader: - return "Saved Instances" + return "Instance Timelines" case let .savedInstance(url): return url.host! case .addSavedInstance: