From ff97b0f76deb4303d7076731082749961eba68b7 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 20 Jan 2020 11:48:47 -0500 Subject: [PATCH] Change saved hashtags/instances to be per-account See #16 --- Tusker/SavedDataManager.swift | 69 +++++++++++++------ .../AddSavedHashtagViewController.swift | 2 +- .../Explore/ExploreViewController.swift | 24 ++++--- .../Screens/FindInstanceViewController.swift | 14 +++- .../HashtagTimelineViewController.swift | 8 +-- .../InstanceTimelineViewController.swift | 16 +++-- 6 files changed, 90 insertions(+), 43 deletions(-) diff --git a/Tusker/SavedDataManager.swift b/Tusker/SavedDataManager.swift index 93ef0959..6d4696f9 100644 --- a/Tusker/SavedDataManager.swift +++ b/Tusker/SavedDataManager.swift @@ -34,51 +34,76 @@ class SavedDataManager: Codable { private init() {} - private var savedHashtags: [Hashtag] = [] { + private var savedHashtags: [String: [Hashtag]] = [:] { didSet { SavedDataManager.save() NotificationCenter.default.post(name: .savedHashtagsChanged, object: nil) } } - var sortedHashtags: [Hashtag] { - return savedHashtags.sorted(by: { $0.name < $1.name }) - } - - private(set) var savedInstances: [URL] = [] { + + private var savedInstances: [String: [URL]] = [:] { didSet { SavedDataManager.save() NotificationCenter.default.post(name: .savedInstancesChanged, object: nil) } } - func isSaved(hashtag: Hashtag) -> Bool { - return savedHashtags.contains(hashtag) + func sortedHashtags(for account: LocalData.UserAccountInfo) -> [Hashtag] { + if let hashtags = savedHashtags[account.id] { + return hashtags.sorted(by: { $0.name < $1.name }) + } else { + return [] + } } - func add(hashtag: Hashtag) { - if isSaved(hashtag: hashtag) { + func isSaved(hashtag: Hashtag, for account: LocalData.UserAccountInfo) -> Bool { + return savedHashtags[account.id]?.contains(hashtag) ?? false + } + + func add(hashtag: Hashtag, for account: LocalData.UserAccountInfo) { + if isSaved(hashtag: hashtag, for: account) { return } - savedHashtags.append(hashtag) + if var saved = savedHashtags[account.id] { + saved.append(hashtag) + savedHashtags[account.id] = saved + } else { + savedHashtags[account.id] = [hashtag] + } } - func remove(hashtag: Hashtag) { - guard isSaved(hashtag: hashtag) else { return } - savedHashtags.removeAll(where: { $0.name == hashtag.name }) + func remove(hashtag: Hashtag, for account: LocalData.UserAccountInfo) { + guard isSaved(hashtag: hashtag, for: account) else { return } + if var saved = savedHashtags[account.id] { + saved.removeAll(where: { $0.name == hashtag.name }) + savedHashtags[account.id] = saved + } } - func isSaved(instance url: URL) -> Bool { - return savedInstances.contains(url) + func savedInstances(for account: LocalData.UserAccountInfo) -> [URL] { + return savedInstances[account.id] ?? [] } - func add(instance url: URL) { - if isSaved(instance: url) { return } - savedInstances.append(url) + func isSaved(instance url: URL, for account: LocalData.UserAccountInfo) -> Bool { + return savedInstances[account.id]?.contains(url) ?? false } - func remove(instance url: URL) { - guard isSaved(instance: url) else { return } - savedInstances.removeAll(where: { $0 == url }) + func add(instance url: URL, for account: LocalData.UserAccountInfo) { + if isSaved(instance: url, for: account) { return } + if var saved = savedInstances[account.id] { + saved.append(url) + savedInstances[account.id] = saved + } else { + savedInstances[account.id] = [url] + } + } + + func remove(instance url: URL, for account: LocalData.UserAccountInfo) { + guard isSaved(instance: url, for: account) else { return } + if var saved = savedInstances[account.id] { + saved.removeAll(where: { $0 == url }) + savedInstances[account.id] = saved + } } } diff --git a/Tusker/Screens/Explore/AddSavedHashtagViewController.swift b/Tusker/Screens/Explore/AddSavedHashtagViewController.swift index d3b5c491..65585f67 100644 --- a/Tusker/Screens/Explore/AddSavedHashtagViewController.swift +++ b/Tusker/Screens/Explore/AddSavedHashtagViewController.swift @@ -52,7 +52,7 @@ class AddSavedHashtagViewController: SearchResultsViewController { extension AddSavedHashtagViewController: SearchResultsViewControllerDelegate { func selectedSearchResult(hashtag: Hashtag) { - SavedDataManager.shared.add(hashtag: hashtag) + SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!) dismiss(animated: true) } } diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index fe820728..0254ddec 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -81,12 +81,14 @@ class ExploreViewController: EnhancedTableViewController { }) dataSource.exploreController = self + let account = mastodonController.accountInfo! + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.bookmarks, .lists, .savedHashtags, .savedInstances]) snapshot.appendItems([.bookmarks], toSection: .bookmarks) snapshot.appendItems([.addList], toSection: .lists) - snapshot.appendItems(SavedDataManager.shared.sortedHashtags.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) - snapshot.appendItems(SavedDataManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) + snapshot.appendItems(SavedDataManager.shared.sortedHashtags(for: account).map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) + snapshot.appendItems(SavedDataManager.shared.savedInstances(for: account).map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) // the initial, static items should not be displayed with an animation UIView.performWithoutAnimation { dataSource.apply(snapshot) @@ -127,16 +129,18 @@ class ExploreViewController: EnhancedTableViewController { } @objc func savedHashtagsChanged() { + let account = mastodonController.accountInfo! var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags)) - snapshot.appendItems(SavedDataManager.shared.sortedHashtags.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) + snapshot.appendItems(SavedDataManager.shared.sortedHashtags(for: account).map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) dataSource.apply(snapshot) } @objc func savedInstancesChanged() { + let account = mastodonController.accountInfo! var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedInstances)) - snapshot.appendItems(SavedDataManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) + snapshot.appendItems(SavedDataManager.shared.savedInstances(for: account).map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) dataSource.apply(snapshot) } @@ -163,11 +167,13 @@ class ExploreViewController: EnhancedTableViewController { } func removeSavedHashtag(_ hashtag: Hashtag) { - SavedDataManager.shared.remove(hashtag: hashtag) + let account = mastodonController.accountInfo! + SavedDataManager.shared.remove(hashtag: hashtag, for: account) } func removeSavedInstance(_ instanceURL: URL) { - SavedDataManager.shared.remove(instance: instanceURL) + let account = mastodonController.accountInfo! + SavedDataManager.shared.remove(instance: instanceURL, for: account) } // MARK: - Table view delegate @@ -217,11 +223,11 @@ class ExploreViewController: EnhancedTableViewController { present(navController, animated: true) case let .savedInstance(url): - show(InstanceTimelineViewController(for: url), sender: nil) + show(InstanceTimelineViewController(for: url, parentMastodonController: mastodonController), sender: nil) case .findInstance: tableView.selectRow(at: nil, animated: true, scrollPosition: .none) - let findController = FindInstanceViewController() + let findController = FindInstanceViewController(parentMastodonController: mastodonController) findController.instanceTimelineDelegate = self let navController = UINavigationController(rootViewController: findController) present(navController, animated: true) @@ -348,7 +354,7 @@ extension ExploreViewController { extension ExploreViewController: InstanceTimelineViewControllerDelegate { func didSaveInstance(url: URL) { dismiss(animated: true) { - self.show(InstanceTimelineViewController(for: url), sender: nil) + self.show(InstanceTimelineViewController(for: url, parentMastodonController: self.mastodonController), sender: nil) } } diff --git a/Tusker/Screens/FindInstanceViewController.swift b/Tusker/Screens/FindInstanceViewController.swift index 70a3efbf..abe07e43 100644 --- a/Tusker/Screens/FindInstanceViewController.swift +++ b/Tusker/Screens/FindInstanceViewController.swift @@ -10,8 +10,20 @@ import UIKit class FindInstanceViewController: InstanceSelectorTableViewController { + weak var parentMastodonController: MastodonController? + var instanceTimelineDelegate: InstanceTimelineViewControllerDelegate? + init(parentMastodonController: MastodonController) { + self.parentMastodonController = parentMastodonController + + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -32,7 +44,7 @@ class FindInstanceViewController: InstanceSelectorTableViewController { extension FindInstanceViewController: InstanceSelectorTableViewControllerDelegate { func didSelectInstance(url: URL) { - let instanceTimelineController = InstanceTimelineViewController(for: url) + let instanceTimelineController = InstanceTimelineViewController(for: url, parentMastodonController: parentMastodonController!) instanceTimelineController.delegate = instanceTimelineDelegate show(instanceTimelineController, sender: self) } diff --git a/Tusker/Screens/Timeline/HashtagTimelineViewController.swift b/Tusker/Screens/Timeline/HashtagTimelineViewController.swift index bf3f5a9b..ab8d5b6c 100644 --- a/Tusker/Screens/Timeline/HashtagTimelineViewController.swift +++ b/Tusker/Screens/Timeline/HashtagTimelineViewController.swift @@ -15,7 +15,7 @@ class HashtagTimelineViewController: TimelineTableViewController { var toggleSaveButton: UIBarButtonItem! var toggleSaveButtonTitle: String { - if SavedDataManager.shared.isSaved(hashtag: hashtag) { + if SavedDataManager.shared.isSaved(hashtag: hashtag, for: mastodonController.accountInfo!) { return NSLocalizedString("Unsave", comment: "unsave hashtag button") } else { return NSLocalizedString("Save", comment: "save hashtag button") @@ -48,10 +48,10 @@ class HashtagTimelineViewController: TimelineTableViewController { // MARK: - Interaction @objc func toggleSaveButtonPressed() { - if SavedDataManager.shared.isSaved(hashtag: hashtag) { - SavedDataManager.shared.remove(hashtag: hashtag) + if SavedDataManager.shared.isSaved(hashtag: hashtag, for: mastodonController.accountInfo!) { + SavedDataManager.shared.remove(hashtag: hashtag, for: mastodonController.accountInfo!) } else { - SavedDataManager.shared.add(hashtag: hashtag) + SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!) } } diff --git a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift index 0c99ae1d..2694bf6f 100644 --- a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift +++ b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift @@ -17,21 +17,25 @@ class InstanceTimelineViewController: TimelineTableViewController { weak var delegate: InstanceTimelineViewControllerDelegate? + weak var parentMastodonController: MastodonController? + let instanceURL: URL let instanceMastodonController: MastodonController var toggleSaveButton: UIBarButtonItem! var toggleSaveButtonTitle: String { - if SavedDataManager.shared.isSaved(instance: instanceURL) { + if SavedDataManager.shared.isSaved(instance: instanceURL, for: parentMastodonController!.accountInfo!) { return NSLocalizedString("Unsave", comment: "unsave instance button") } else { return NSLocalizedString("Save", comment: "save instance button") } } - init(for url: URL) { - self.instanceURL = url + init(for url: URL, parentMastodonController: MastodonController) { + self.parentMastodonController = parentMastodonController + self.instanceURL = url + // the timeline VC only stores a weak reference to it, so we need to store a strong reference to make sure it's not released immediately instanceMastodonController = MastodonController(instanceURL: url) @@ -72,11 +76,11 @@ class InstanceTimelineViewController: TimelineTableViewController { // MARK: - Interaction @objc func toggleSaveButtonPressed() { - if SavedDataManager.shared.isSaved(instance: instanceURL) { - SavedDataManager.shared.remove(instance: instanceURL) + if SavedDataManager.shared.isSaved(instance: instanceURL, for: parentMastodonController!.accountInfo!) { + SavedDataManager.shared.remove(instance: instanceURL, for: parentMastodonController!.accountInfo!) delegate?.didUnsaveInstance(url: instanceURL) } else { - SavedDataManager.shared.add(instance: instanceURL) + SavedDataManager.shared.add(instance: instanceURL, for: parentMastodonController!.accountInfo!) delegate?.didSaveInstance(url: instanceURL) } }