Change saved hashtags/instances to be per-account

See #16
This commit is contained in:
Shadowfacts 2020-01-20 11:48:47 -05:00
parent 26f1aafa15
commit ff97b0f76d
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
6 changed files with 90 additions and 43 deletions

View File

@ -34,51 +34,76 @@ class SavedDataManager: Codable {
private init() {} private init() {}
private var savedHashtags: [Hashtag] = [] { private var savedHashtags: [String: [Hashtag]] = [:] {
didSet { didSet {
SavedDataManager.save() SavedDataManager.save()
NotificationCenter.default.post(name: .savedHashtagsChanged, object: nil) 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 { didSet {
SavedDataManager.save() SavedDataManager.save()
NotificationCenter.default.post(name: .savedInstancesChanged, object: nil) NotificationCenter.default.post(name: .savedInstancesChanged, object: nil)
} }
} }
func isSaved(hashtag: Hashtag) -> Bool { func sortedHashtags(for account: LocalData.UserAccountInfo) -> [Hashtag] {
return savedHashtags.contains(hashtag) if let hashtags = savedHashtags[account.id] {
return hashtags.sorted(by: { $0.name < $1.name })
} else {
return []
}
} }
func add(hashtag: Hashtag) { func isSaved(hashtag: Hashtag, for account: LocalData.UserAccountInfo) -> Bool {
if isSaved(hashtag: hashtag) { return savedHashtags[account.id]?.contains(hashtag) ?? false
}
func add(hashtag: Hashtag, for account: LocalData.UserAccountInfo) {
if isSaved(hashtag: hashtag, for: account) {
return 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) { func remove(hashtag: Hashtag, for account: LocalData.UserAccountInfo) {
guard isSaved(hashtag: hashtag) else { return } guard isSaved(hashtag: hashtag, for: account) else { return }
savedHashtags.removeAll(where: { $0.name == hashtag.name }) if var saved = savedHashtags[account.id] {
saved.removeAll(where: { $0.name == hashtag.name })
savedHashtags[account.id] = saved
}
} }
func isSaved(instance url: URL) -> Bool { func savedInstances(for account: LocalData.UserAccountInfo) -> [URL] {
return savedInstances.contains(url) return savedInstances[account.id] ?? []
} }
func add(instance url: URL) { func isSaved(instance url: URL, for account: LocalData.UserAccountInfo) -> Bool {
if isSaved(instance: url) { return } return savedInstances[account.id]?.contains(url) ?? false
savedInstances.append(url)
} }
func remove(instance url: URL) { func add(instance url: URL, for account: LocalData.UserAccountInfo) {
guard isSaved(instance: url) else { return } if isSaved(instance: url, for: account) { return }
savedInstances.removeAll(where: { $0 == url }) 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
}
} }
} }

View File

@ -52,7 +52,7 @@ class AddSavedHashtagViewController: SearchResultsViewController {
extension AddSavedHashtagViewController: SearchResultsViewControllerDelegate { extension AddSavedHashtagViewController: SearchResultsViewControllerDelegate {
func selectedSearchResult(hashtag: Hashtag) { func selectedSearchResult(hashtag: Hashtag) {
SavedDataManager.shared.add(hashtag: hashtag) SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!)
dismiss(animated: true) dismiss(animated: true)
} }
} }

View File

@ -81,12 +81,14 @@ class ExploreViewController: EnhancedTableViewController {
}) })
dataSource.exploreController = self dataSource.exploreController = self
let account = mastodonController.accountInfo!
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.bookmarks, .lists, .savedHashtags, .savedInstances]) snapshot.appendSections([.bookmarks, .lists, .savedHashtags, .savedInstances])
snapshot.appendItems([.bookmarks], toSection: .bookmarks) snapshot.appendItems([.bookmarks], toSection: .bookmarks)
snapshot.appendItems([.addList], toSection: .lists) snapshot.appendItems([.addList], toSection: .lists)
snapshot.appendItems(SavedDataManager.shared.sortedHashtags.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) snapshot.appendItems(SavedDataManager.shared.sortedHashtags(for: account).map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags)
snapshot.appendItems(SavedDataManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) snapshot.appendItems(SavedDataManager.shared.savedInstances(for: account).map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances)
// the initial, static items should not be displayed with an animation // the initial, static items should not be displayed with an animation
UIView.performWithoutAnimation { UIView.performWithoutAnimation {
dataSource.apply(snapshot) dataSource.apply(snapshot)
@ -127,16 +129,18 @@ class ExploreViewController: EnhancedTableViewController {
} }
@objc func savedHashtagsChanged() { @objc func savedHashtagsChanged() {
let account = mastodonController.accountInfo!
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags)) 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) dataSource.apply(snapshot)
} }
@objc func savedInstancesChanged() { @objc func savedInstancesChanged() {
let account = mastodonController.accountInfo!
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedInstances)) 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) dataSource.apply(snapshot)
} }
@ -163,11 +167,13 @@ class ExploreViewController: EnhancedTableViewController {
} }
func removeSavedHashtag(_ hashtag: Hashtag) { func removeSavedHashtag(_ hashtag: Hashtag) {
SavedDataManager.shared.remove(hashtag: hashtag) let account = mastodonController.accountInfo!
SavedDataManager.shared.remove(hashtag: hashtag, for: account)
} }
func removeSavedInstance(_ instanceURL: URL) { func removeSavedInstance(_ instanceURL: URL) {
SavedDataManager.shared.remove(instance: instanceURL) let account = mastodonController.accountInfo!
SavedDataManager.shared.remove(instance: instanceURL, for: account)
} }
// MARK: - Table view delegate // MARK: - Table view delegate
@ -217,11 +223,11 @@ class ExploreViewController: EnhancedTableViewController {
present(navController, animated: true) present(navController, animated: true)
case let .savedInstance(url): case let .savedInstance(url):
show(InstanceTimelineViewController(for: url), sender: nil) show(InstanceTimelineViewController(for: url, parentMastodonController: mastodonController), sender: nil)
case .findInstance: case .findInstance:
tableView.selectRow(at: nil, animated: true, scrollPosition: .none) tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
let findController = FindInstanceViewController() let findController = FindInstanceViewController(parentMastodonController: mastodonController)
findController.instanceTimelineDelegate = self findController.instanceTimelineDelegate = self
let navController = UINavigationController(rootViewController: findController) let navController = UINavigationController(rootViewController: findController)
present(navController, animated: true) present(navController, animated: true)
@ -348,7 +354,7 @@ extension ExploreViewController {
extension ExploreViewController: InstanceTimelineViewControllerDelegate { extension ExploreViewController: InstanceTimelineViewControllerDelegate {
func didSaveInstance(url: URL) { func didSaveInstance(url: URL) {
dismiss(animated: true) { dismiss(animated: true) {
self.show(InstanceTimelineViewController(for: url), sender: nil) self.show(InstanceTimelineViewController(for: url, parentMastodonController: self.mastodonController), sender: nil)
} }
} }

View File

@ -10,8 +10,20 @@ import UIKit
class FindInstanceViewController: InstanceSelectorTableViewController { class FindInstanceViewController: InstanceSelectorTableViewController {
weak var parentMastodonController: MastodonController?
var instanceTimelineDelegate: InstanceTimelineViewControllerDelegate? 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() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -32,7 +44,7 @@ class FindInstanceViewController: InstanceSelectorTableViewController {
extension FindInstanceViewController: InstanceSelectorTableViewControllerDelegate { extension FindInstanceViewController: InstanceSelectorTableViewControllerDelegate {
func didSelectInstance(url: URL) { func didSelectInstance(url: URL) {
let instanceTimelineController = InstanceTimelineViewController(for: url) let instanceTimelineController = InstanceTimelineViewController(for: url, parentMastodonController: parentMastodonController!)
instanceTimelineController.delegate = instanceTimelineDelegate instanceTimelineController.delegate = instanceTimelineDelegate
show(instanceTimelineController, sender: self) show(instanceTimelineController, sender: self)
} }

View File

@ -15,7 +15,7 @@ class HashtagTimelineViewController: TimelineTableViewController {
var toggleSaveButton: UIBarButtonItem! var toggleSaveButton: UIBarButtonItem!
var toggleSaveButtonTitle: String { 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") return NSLocalizedString("Unsave", comment: "unsave hashtag button")
} else { } else {
return NSLocalizedString("Save", comment: "save hashtag button") return NSLocalizedString("Save", comment: "save hashtag button")
@ -48,10 +48,10 @@ class HashtagTimelineViewController: TimelineTableViewController {
// MARK: - Interaction // MARK: - Interaction
@objc func toggleSaveButtonPressed() { @objc func toggleSaveButtonPressed() {
if SavedDataManager.shared.isSaved(hashtag: hashtag) { if SavedDataManager.shared.isSaved(hashtag: hashtag, for: mastodonController.accountInfo!) {
SavedDataManager.shared.remove(hashtag: hashtag) SavedDataManager.shared.remove(hashtag: hashtag, for: mastodonController.accountInfo!)
} else { } else {
SavedDataManager.shared.add(hashtag: hashtag) SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!)
} }
} }

View File

@ -17,19 +17,23 @@ class InstanceTimelineViewController: TimelineTableViewController {
weak var delegate: InstanceTimelineViewControllerDelegate? weak var delegate: InstanceTimelineViewControllerDelegate?
weak var parentMastodonController: MastodonController?
let instanceURL: URL let instanceURL: URL
let instanceMastodonController: MastodonController let instanceMastodonController: MastodonController
var toggleSaveButton: UIBarButtonItem! var toggleSaveButton: UIBarButtonItem!
var toggleSaveButtonTitle: String { 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") return NSLocalizedString("Unsave", comment: "unsave instance button")
} else { } else {
return NSLocalizedString("Save", comment: "save instance button") return NSLocalizedString("Save", comment: "save instance button")
} }
} }
init(for url: URL) { init(for url: URL, parentMastodonController: MastodonController) {
self.parentMastodonController = parentMastodonController
self.instanceURL = url 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 // 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
@ -72,11 +76,11 @@ class InstanceTimelineViewController: TimelineTableViewController {
// MARK: - Interaction // MARK: - Interaction
@objc func toggleSaveButtonPressed() { @objc func toggleSaveButtonPressed() {
if SavedDataManager.shared.isSaved(instance: instanceURL) { if SavedDataManager.shared.isSaved(instance: instanceURL, for: parentMastodonController!.accountInfo!) {
SavedDataManager.shared.remove(instance: instanceURL) SavedDataManager.shared.remove(instance: instanceURL, for: parentMastodonController!.accountInfo!)
delegate?.didUnsaveInstance(url: instanceURL) delegate?.didUnsaveInstance(url: instanceURL)
} else { } else {
SavedDataManager.shared.add(instance: instanceURL) SavedDataManager.shared.add(instance: instanceURL, for: parentMastodonController!.accountInfo!)
delegate?.didSaveInstance(url: instanceURL) delegate?.didSaveInstance(url: instanceURL)
} }
} }