diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 615568e9..1bca044d 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -156,10 +156,9 @@ D67C57B221E28FAD00C3118B /* ComposeStatusReplyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */; }; D67C57B421E2910700C3118B /* ComposeStatusReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */; }; D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; }; - D6945C2F23AC47C3005C403C /* SavedHashtagsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */; }; + D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C2E23AC47C3005C403C /* SavedDataManager.swift */; }; D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */; }; D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; }; - D6945C3623AC6C09005C403C /* SavedInstancesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */; }; D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; }; D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; }; D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; }; @@ -432,10 +431,9 @@ D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeStatusReplyView.xib; sourceTree = ""; }; D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusReplyView.swift; sourceTree = ""; }; D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = ""; }; - D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedHashtagsManager.swift; sourceTree = ""; }; + D6945C2E23AC47C3005C403C /* SavedDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedDataManager.swift; sourceTree = ""; }; D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewController.swift; sourceTree = ""; }; D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = ""; }; - D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedInstancesManager.swift; sourceTree = ""; }; D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = ""; }; D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; }; D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = ""; }; @@ -1193,8 +1191,7 @@ D6AC956623C4347E008C9946 /* SceneDelegate.swift */, D64D0AAC2128D88B005A6F37 /* LocalData.swift */, D627FF75217E923E00CC0648 /* DraftsManager.swift */, - D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */, - D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */, + D6945C2E23AC47C3005C403C /* SavedDataManager.swift */, D6028B9A2150811100F223B9 /* MastodonCache.swift */, D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */, D6F1F84E2193B9BE00F5FE67 /* Caching */, @@ -1641,7 +1638,6 @@ D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */, 0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */, D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */, - D6945C3623AC6C09005C403C /* SavedInstancesManager.swift in Sources */, D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */, D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */, D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */, @@ -1697,7 +1693,7 @@ 0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */, D627943923A553B600D38C68 /* UnbookmarkStatusActivity.swift in Sources */, D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */, - D6945C2F23AC47C3005C403C /* SavedHashtagsManager.swift in Sources */, + D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */, D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */, D6B053AE23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift in Sources */, D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */, diff --git a/Tusker/SavedDataManager.swift b/Tusker/SavedDataManager.swift new file mode 100644 index 00000000..93ef0959 --- /dev/null +++ b/Tusker/SavedDataManager.swift @@ -0,0 +1,88 @@ +// +// SavedDataManager.swift +// Tusker +// +// Created by Shadowfacts on 12/19/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import Foundation +import Pachyderm + +class SavedDataManager: Codable { + private(set) static var shared: SavedDataManager = load() + + private static var documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + private static var archiveURL = SavedDataManager.documentsDirectory.appendingPathComponent("saved_data").appendingPathExtension("plist") + + static func save() { + DispatchQueue.global(qos: .utility).async { + let encoder = PropertyListEncoder() + let data = try? encoder.encode(shared) + try? data?.write(to: archiveURL, options: .noFileProtection) + } + } + + static func load() -> SavedDataManager { + let decoder = PropertyListDecoder() + if let data = try? Data(contentsOf: archiveURL), + let savedHashtagsManager = try? decoder.decode(Self.self, from: data) { + return savedHashtagsManager + } + return SavedDataManager() + } + + private init() {} + + private var savedHashtags: [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] = [] { + didSet { + SavedDataManager.save() + NotificationCenter.default.post(name: .savedInstancesChanged, object: nil) + } + } + + func isSaved(hashtag: Hashtag) -> Bool { + return savedHashtags.contains(hashtag) + } + + func add(hashtag: Hashtag) { + if isSaved(hashtag: hashtag) { + return + } + savedHashtags.append(hashtag) + } + + func remove(hashtag: Hashtag) { + guard isSaved(hashtag: hashtag) else { return } + savedHashtags.removeAll(where: { $0.name == hashtag.name }) + } + + func isSaved(instance url: URL) -> Bool { + return savedInstances.contains(url) + } + + func add(instance url: URL) { + if isSaved(instance: url) { return } + savedInstances.append(url) + } + + func remove(instance url: URL) { + guard isSaved(instance: url) else { return } + savedInstances.removeAll(where: { $0 == url }) + } +} + +extension Foundation.Notification.Name { + static let savedHashtagsChanged = Notification.Name("savedHashtagsChanged") + static let savedInstancesChanged = Notification.Name("savedInstancesChanged") +} diff --git a/Tusker/SavedHashtagsManager.swift b/Tusker/SavedHashtagsManager.swift deleted file mode 100644 index cf91aa9e..00000000 --- a/Tusker/SavedHashtagsManager.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// SavedHashtagsManager.swift -// Tusker -// -// Created by Shadowfacts on 12/19/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import Foundation -import Pachyderm - -class SavedHashtagsManager: Codable { - private(set) static var shared: SavedHashtagsManager = load() - - private static var documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - private static var archiveURL = SavedHashtagsManager.documentsDirectory.appendingPathComponent("saved_hashtags").appendingPathExtension("plist") - - static func save() { - DispatchQueue.global(qos: .utility).async { - let encoder = PropertyListEncoder() - let data = try? encoder.encode(shared) - try? data?.write(to: archiveURL, options: .noFileProtection) - } - } - - static func load() -> SavedHashtagsManager { - let decoder = PropertyListDecoder() - if let data = try? Data(contentsOf: archiveURL), - let savedHashtagsManager = try? decoder.decode(Self.self, from: data) { - return savedHashtagsManager - } - return SavedHashtagsManager() - } - - private init() {} - - private var savedHashtags: [Hashtag] = [] - var sorted: [Hashtag] { - return savedHashtags.sorted(by: { $0.name < $1.name }) - } - - func isSaved(_ hashtag: Hashtag) -> Bool { - return savedHashtags.contains(hashtag) - } - - func add(_ hashtag: Hashtag) { - if isSaved(hashtag) { - return - } - savedHashtags.append(hashtag) - SavedHashtagsManager.save() - NotificationCenter.default.post(name: .savedHashtagsChanged, object: nil) - } - - func remove(_ hashtag: Hashtag) { - guard isSaved(hashtag) else { return } - savedHashtags.removeAll(where: { $0.name == hashtag.name }) - SavedHashtagsManager.save() - NotificationCenter.default.post(name: .savedHashtagsChanged, object: nil) - } -} - -extension Foundation.Notification.Name { - static let savedHashtagsChanged = Notification.Name("savedHashtagsChanged") -} diff --git a/Tusker/SavedInstancesManager.swift b/Tusker/SavedInstancesManager.swift deleted file mode 100644 index 7bc466c1..00000000 --- a/Tusker/SavedInstancesManager.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// SavedInstancesManager.swift -// Tusker -// -// Created by Shadowfacts on 12/19/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import Foundation - -class SavedInstanceManager: Codable { - private(set) static var shared: SavedInstanceManager = load() - - private static var documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - private static var archiveURL = SavedInstanceManager.documentsDirectory.appendingPathComponent("saved_instances").appendingPathExtension("plist") - - static func save() { - DispatchQueue.global(qos: .utility).async { - let encoder = PropertyListEncoder() - let data = try? encoder.encode(shared) - try? data?.write(to: archiveURL, options: .noFileProtection) - } - } - - static func load() -> SavedInstanceManager { - let decoder = PropertyListDecoder() - if let data = try? Data(contentsOf: archiveURL), - let savedInstanceManager = try? decoder.decode(Self.self, from: data) { - return savedInstanceManager - } - return SavedInstanceManager() - } - - private init() {} - - private(set) var savedInstances: [URL] = [] - - func isSaved(_ url: URL) -> Bool { - return savedInstances.contains(url) - } - - func add(_ url: URL) { - if isSaved(url) { - return - } - savedInstances.append(url) - SavedInstanceManager.save() - NotificationCenter.default.post(name: .savedInstancesChanged, object: nil) - } - - func remove(_ url: URL) { - guard isSaved(url) else { return } - savedInstances.removeAll(where: { $0 == url }) - SavedInstanceManager.save() - NotificationCenter.default.post(name: .savedInstancesChanged, object: nil) - } -} - -extension Notification.Name { - static let savedInstancesChanged = Notification.Name("savedInstancesChanged") -} diff --git a/Tusker/Screens/Explore/AddSavedHashtagViewController.swift b/Tusker/Screens/Explore/AddSavedHashtagViewController.swift index f45eb025..d3b5c491 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) { - SavedHashtagsManager.shared.add(hashtag) + SavedDataManager.shared.add(hashtag: hashtag) dismiss(animated: true) } } diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index c278c2b4..fe820728 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -85,8 +85,8 @@ class ExploreViewController: EnhancedTableViewController { snapshot.appendSections([.bookmarks, .lists, .savedHashtags, .savedInstances]) snapshot.appendItems([.bookmarks], toSection: .bookmarks) snapshot.appendItems([.addList], toSection: .lists) - snapshot.appendItems(SavedHashtagsManager.shared.sorted.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) - snapshot.appendItems(SavedInstanceManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) + snapshot.appendItems(SavedDataManager.shared.sortedHashtags.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) + snapshot.appendItems(SavedDataManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) // the initial, static items should not be displayed with an animation UIView.performWithoutAnimation { dataSource.apply(snapshot) @@ -129,14 +129,14 @@ class ExploreViewController: EnhancedTableViewController { @objc func savedHashtagsChanged() { var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags)) - snapshot.appendItems(SavedHashtagsManager.shared.sorted.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) + snapshot.appendItems(SavedDataManager.shared.sortedHashtags.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags) dataSource.apply(snapshot) } @objc func savedInstancesChanged() { var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedInstances)) - snapshot.appendItems(SavedInstanceManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) + snapshot.appendItems(SavedDataManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances) dataSource.apply(snapshot) } @@ -163,11 +163,11 @@ class ExploreViewController: EnhancedTableViewController { } func removeSavedHashtag(_ hashtag: Hashtag) { - SavedHashtagsManager.shared.remove(hashtag) + SavedDataManager.shared.remove(hashtag: hashtag) } func removeSavedInstance(_ instanceURL: URL) { - SavedInstanceManager.shared.remove(instanceURL) + SavedDataManager.shared.remove(instance: instanceURL) } // MARK: - Table view delegate diff --git a/Tusker/Screens/Onboarding/InstanceSelectorTableViewController.swift b/Tusker/Screens/Onboarding/InstanceSelectorTableViewController.swift index 11681d23..b77e8bc9 100644 --- a/Tusker/Screens/Onboarding/InstanceSelectorTableViewController.swift +++ b/Tusker/Screens/Onboarding/InstanceSelectorTableViewController.swift @@ -55,7 +55,7 @@ class InstanceSelectorTableViewController: UITableViewController { tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 120 - dataSource = DataSource(tableView: tableView, cellProvider: { [weak self] (tableView, indexPath, item) -> UITableViewCell? in + dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in switch item { case let .selected(instance): let cell = tableView.dequeueReusableCell(withIdentifier: instanceCell, for: indexPath) as! InstanceTableViewCell diff --git a/Tusker/Screens/Timeline/HashtagTimelineViewController.swift b/Tusker/Screens/Timeline/HashtagTimelineViewController.swift index 3dcd8787..bf3f5a9b 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 SavedHashtagsManager.shared.isSaved(hashtag) { + if SavedDataManager.shared.isSaved(hashtag: hashtag) { 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 SavedHashtagsManager.shared.isSaved(hashtag) { - SavedHashtagsManager.shared.remove(hashtag) + if SavedDataManager.shared.isSaved(hashtag: hashtag) { + SavedDataManager.shared.remove(hashtag: hashtag) } else { - SavedHashtagsManager.shared.add(hashtag) + SavedDataManager.shared.add(hashtag: hashtag) } } diff --git a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift index bd249a80..0c99ae1d 100644 --- a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift +++ b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift @@ -22,7 +22,7 @@ class InstanceTimelineViewController: TimelineTableViewController { var toggleSaveButton: UIBarButtonItem! var toggleSaveButtonTitle: String { - if SavedInstanceManager.shared.isSaved(instanceURL) { + if SavedDataManager.shared.isSaved(instance: instanceURL) { return NSLocalizedString("Unsave", comment: "unsave instance button") } else { return NSLocalizedString("Save", comment: "save instance button") @@ -72,11 +72,11 @@ class InstanceTimelineViewController: TimelineTableViewController { // MARK: - Interaction @objc func toggleSaveButtonPressed() { - if SavedInstanceManager.shared.isSaved(instanceURL) { - SavedInstanceManager.shared.remove(instanceURL) + if SavedDataManager.shared.isSaved(instance: instanceURL) { + SavedDataManager.shared.remove(instance: instanceURL) delegate?.didUnsaveInstance(url: instanceURL) } else { - SavedInstanceManager.shared.add(instanceURL) + SavedDataManager.shared.add(instance: instanceURL) delegate?.didSaveInstance(url: instanceURL) } }