[ExpFeat] Adds filter button to change RepairSaveStatesViewController’s date range

* Recent (Last month)
* All
This commit is contained in:
Riley Testut 2023-08-11 18:05:35 -05:00
parent d5f910ff00
commit 5c7e3cc5b9
2 changed files with 126 additions and 11 deletions

View File

@ -422,6 +422,7 @@ private extension RepairDatabaseViewController
Logger.database.info("Finished repairing Games and GameSaves, reviewing recent SaveStates...") Logger.database.info("Finished repairing Games and GameSaves, reviewing recent SaveStates...")
let viewController = ReviewSaveStatesViewController() let viewController = ReviewSaveStatesViewController()
viewController.filter = .sinceLastBeta
viewController.completionHandler = { [weak self] in viewController.completionHandler = { [weak self] in
self?.finish() self?.finish()
} }

View File

@ -12,6 +12,16 @@ import OSLog
import Harmony import Harmony
import Roxas import Roxas
extension ReviewSaveStatesViewController
{
enum Filter
{
case recent
case all
case sinceLastBeta
}
}
extension RecordFlags extension RecordFlags
{ {
static let isGameRelationshipVerified = RecordFlags(rawValue: 1 << 0) static let isGameRelationshipVerified = RecordFlags(rawValue: 1 << 0)
@ -19,6 +29,12 @@ extension RecordFlags
class ReviewSaveStatesViewController: UITableViewController class ReviewSaveStatesViewController: UITableViewController
{ {
var filter: Filter = .recent {
didSet {
self.updateDataSource()
}
}
var completionHandler: (() -> Void)? var completionHandler: (() -> Void)?
private lazy var managedObjectContext = DatabaseManager.shared.newBackgroundSavingViewContext() private lazy var managedObjectContext = DatabaseManager.shared.newBackgroundSavingViewContext()
@ -27,6 +43,8 @@ class ReviewSaveStatesViewController: UITableViewController
private lazy var descriptionDataSource = self.makeDescriptionDataSource() private lazy var descriptionDataSource = self.makeDescriptionDataSource()
private lazy var saveStatesDataSource = self.makeSaveStatesDataSource() private lazy var saveStatesDataSource = self.makeSaveStatesDataSource()
private weak var _parentNavigationController: UINavigationController?
init() init()
{ {
super.init(style: .insetGrouped) super.init(style: .insetGrouped)
@ -60,8 +78,36 @@ class ReviewSaveStatesViewController: UITableViewController
{ {
super.viewWillAppear(animated) super.viewWillAppear(animated)
// Must set parent's navigationItem.title for when we're contained in SwiftUI View. if let parent = self.parent, parent.navigationItem.title == nil
self.parent?.navigationItem.title = NSLocalizedString("Review Save States", comment: "") {
// Must change parent's navigationItem when we're contained in SwiftUI View.
parent.navigationItem.title = NSLocalizedString("Review Save States", comment: "")
parent.navigationItem.rightBarButtonItem = self.makeFilterButton()
}
}
override func viewWillDisappear(_ animated: Bool)
{
super.viewWillDisappear(animated)
_parentNavigationController = self.parent?.navigationController
}
override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
switch self.filter
{
case .all, .recent:
if self.parent == nil || self.parent?.parent == nil
{
// Only finish if we're popped off navigation controller.
self.finish()
}
case .sinceLastBeta: break
}
} }
} }
@ -83,13 +129,7 @@ private extension ReviewSaveStatesViewController
func makeSaveStatesDataSource() -> RSTFetchedResultsTableViewPrefetchingDataSource<SaveState, UIImage> func makeSaveStatesDataSource() -> RSTFetchedResultsTableViewPrefetchingDataSource<SaveState, UIImage>
{ {
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: -1, to: Date()) ?? Date().addingTimeInterval(-1 * 60 * 60 * 24 * 30) let fetchedResultsController = self.makeSaveStatesFetchedResultsController()
let fetchRequest = SaveState.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K > %@", #keyPath(SaveState.modifiedDate), oneMonthAgo as NSDate)
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \SaveState.game?.name, ascending: true), NSSortDescriptor(keyPath: \SaveState.modifiedDate, ascending: false)]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: #keyPath(SaveState.game.name), cacheName: nil)
let dataSource = RSTFetchedResultsTableViewPrefetchingDataSource<SaveState, UIImage>(fetchedResultsController: fetchedResultsController) let dataSource = RSTFetchedResultsTableViewPrefetchingDataSource<SaveState, UIImage>(fetchedResultsController: fetchedResultsController)
dataSource.cellConfigurationHandler = { (cell, saveState, indexPath) in dataSource.cellConfigurationHandler = { (cell, saveState, indexPath) in
@ -140,6 +180,67 @@ private extension ReviewSaveStatesViewController
return dataSource return dataSource
} }
func makeSaveStatesFetchedResultsController() -> NSFetchedResultsController<SaveState>
{
let fetchRequest = SaveState.fetchRequest()
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \SaveState.game?.name, ascending: true), NSSortDescriptor(keyPath: \SaveState.modifiedDate, ascending: false)]
let predicate = NSPredicate(format: "%K != %@", #keyPath(SaveState.type), SaveStateType.auto.rawValue as NSNumber)
switch self.filter
{
case .recent:
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: -1, to: Date()) ?? Date().addingTimeInterval(-1 * 60 * 60 * 24 * 30)
let recentPredicate = NSPredicate(format: "%K > %@", #keyPath(SaveState.modifiedDate), oneMonthAgo as NSDate)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, recentPredicate])
case .all:
fetchRequest.predicate = predicate
case .sinceLastBeta:
let dateComponents = DateComponents(year: 2023, month: 7, day: 18, hour: 0, minute: 0, second: 0)
let lastBetaDate = Calendar.current.date(from: dateComponents) ?? Date().addingTimeInterval(-1 * 60 * 60 * 24 * 45)
let sinceLastBetaPredicate = NSPredicate(format: "%K > %@", #keyPath(SaveState.modifiedDate), lastBetaDate as NSDate)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, sinceLastBetaPredicate])
}
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: #keyPath(SaveState.game.name), cacheName: nil)
return fetchedResultsController
}
func updateDataSource()
{
let fetchedResultsController = self.makeSaveStatesFetchedResultsController()
self.saveStatesDataSource.fetchedResultsController = fetchedResultsController
}
func makeFilterButton() -> UIBarButtonItem
{
let recentAction = UIAction(title: NSLocalizedString("Past Month", comment: ""), image: UIImage(systemName: "calendar")) { [weak self] _ in
self?.filter = .recent
}
let allAction = UIAction(title: NSLocalizedString("All Time", comment: ""), image: UIImage(systemName: "clock")) { [weak self] _ in
self?.filter = .all
}
var options: UIMenu.Options = []
if #available(iOS 15, *)
{
options = .singleSelection
recentAction.state = self.filter == .recent ? .on : .off
allAction.state = self.filter == .all ? .on : .off
}
let filterMenu = UIMenu(options: options, children: [recentAction, allAction])
let filterButton = UIBarButtonItem(title: NSLocalizedString("Filter", comment: ""), image: UIImage(systemName: "calendar.badge.clock"), menu: filterMenu)
return filterButton
}
} }
private extension ReviewSaveStatesViewController private extension ReviewSaveStatesViewController
@ -190,9 +291,22 @@ private extension ReviewSaveStatesViewController
self.managedObjectContext.perform { self.managedObjectContext.perform {
do do
{ {
let saveStates: [SaveState]?
switch self.filter
{
case .recent, .all:
// Only upload metadata for changed SaveStates.
saveStates = self.managedObjectContext.updatedObjects.compactMap { $0 as? SaveState }
case .sinceLastBeta:
// Upload metadata for _all_ SaveStates.
saveStates = self.saveStatesDataSource.fetchedResultsController.fetchedObjects
}
try self.managedObjectContext.save() try self.managedObjectContext.save()
if let saveStates = self.saveStatesDataSource.fetchedResultsController.fetchedObjects, let coordinator = SyncManager.shared.coordinator if let saveStates = saveStates, let coordinator = SyncManager.shared.coordinator
{ {
let records = try coordinator.recordController.fetchRecords(for: saveStates) let records = try coordinator.recordController.fetchRecords(for: saveStates)
if let context = records.first?.recordedObject?.managedObjectContext if let context = records.first?.recordedObject?.managedObjectContext
@ -224,7 +338,7 @@ private extension ReviewSaveStatesViewController
self.navigationItem.rightBarButtonItem?.isIndicatingActivity = false self.navigationItem.rightBarButtonItem?.isIndicatingActivity = false
let alertController = UIAlertController(title: NSLocalizedString("Unable to Save Changes", comment: ""), error: error) let alertController = UIAlertController(title: NSLocalizedString("Unable to Save Changes", comment: ""), error: error)
self.present(alertController, animated: true) (self._parentNavigationController ?? self).present(alertController, animated: true)
} }
} }
} }