Adds basic GameSyncStatusViewController to view status of game-related records
This commit is contained in:
parent
5354d779c1
commit
ca4ccfc3ae
@ -99,6 +99,7 @@
|
||||
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; };
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */; };
|
||||
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */; };
|
||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */; };
|
||||
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */; };
|
||||
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */; };
|
||||
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; };
|
||||
@ -247,6 +248,7 @@
|
||||
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridMenuViewController.swift; sourceTree = "<group>"; };
|
||||
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; };
|
||||
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgedTableViewCell.swift; sourceTree = "<group>"; };
|
||||
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSyncStatusViewController.swift; sourceTree = "<group>"; };
|
||||
BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuController.swift; sourceTree = "<group>"; };
|
||||
BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCalloutView.swift; sourceTree = "<group>"; };
|
||||
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadata.swift; sourceTree = "<group>"; };
|
||||
@ -375,6 +377,7 @@
|
||||
children = (
|
||||
BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */,
|
||||
BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */,
|
||||
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */,
|
||||
);
|
||||
path = Syncing;
|
||||
sourceTree = "<group>";
|
||||
@ -930,6 +933,7 @@
|
||||
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
|
||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,
|
||||
|
||||
@ -828,6 +828,9 @@
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="OnX-sX-bHK" kind="show" identifier="showGame" id="vUN-0T-oaK"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
@ -840,5 +843,43 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3351" y="1872"/>
|
||||
</scene>
|
||||
<!--Game Sync Status View Controller-->
|
||||
<scene sceneID="iQk-cq-qsQ">
|
||||
<objects>
|
||||
<tableViewController id="OnX-sX-bHK" customClass="GameSyncStatusViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="AFt-Hn-fzR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="RWL-4W-NpH" style="IBUITableViewCellStyleDefault" id="q5G-Db-MXt">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="q5G-Db-MXt" id="Ric-Km-AWj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Game" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RWL-4W-NpH">
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="OnX-sX-bHK" id="TuS-hI-T9X"/>
|
||||
<outlet property="delegate" destination="OnX-sX-bHK" id="vII-vu-IIw"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FHk-WU-IoC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4206" y="1872"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
154
Delta/Settings/Syncing/GameSyncStatusViewController.swift
Normal file
154
Delta/Settings/Syncing/GameSyncStatusViewController.swift
Normal file
@ -0,0 +1,154 @@
|
||||
//
|
||||
// GameSyncStatusViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/20/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
extension GameSyncStatusViewController
|
||||
{
|
||||
private enum Section: Int, CaseIterable
|
||||
{
|
||||
case game
|
||||
case saveStates
|
||||
case cheats
|
||||
}
|
||||
}
|
||||
|
||||
class GameSyncStatusViewController: UITableViewController
|
||||
{
|
||||
var game: Game!
|
||||
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
|
||||
private let dateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.dateStyle = .short
|
||||
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
private var recordsByObjectURI = [URL: Record<NSManagedObject>]()
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.title = self.game.name
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
self.fetchRecords()
|
||||
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
}
|
||||
|
||||
private extension GameSyncStatusViewController
|
||||
{
|
||||
private func makeDataSource() -> RSTCompositeTableViewDataSource<NSManagedObject>
|
||||
{
|
||||
func configure(_ cell: UITableViewCell, recordedObject: NSManagedObject)
|
||||
{
|
||||
if let record = self.recordsByObjectURI[recordedObject.objectID.uriRepresentation()], record.isConflicted
|
||||
{
|
||||
cell.textLabel?.textColor = .red
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.textColor = .darkText
|
||||
}
|
||||
}
|
||||
|
||||
let gameDataSource = RSTArrayTableViewDataSource<Game>(items: [self.game])
|
||||
gameDataSource.cellConfigurationHandler = { (cell, game, indexPath) in
|
||||
cell.textLabel?.text = game.name
|
||||
|
||||
configure(cell, recordedObject: game)
|
||||
}
|
||||
|
||||
let saveStatesFetchRequest = SaveState.fetchRequest() as NSFetchRequest<SaveState>
|
||||
saveStatesFetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(SaveState.game), self.game)
|
||||
saveStatesFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \SaveState.creationDate, ascending: true)]
|
||||
|
||||
let saveStatesDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: saveStatesFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
saveStatesDataSource.cellConfigurationHandler = { (cell, saveState, indexPath) in
|
||||
if let name = saveState.name
|
||||
{
|
||||
cell.textLabel?.text = name
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.text = self.dateFormatter.string(from: saveState.modifiedDate)
|
||||
}
|
||||
|
||||
configure(cell, recordedObject: saveState)
|
||||
}
|
||||
|
||||
let cheatsFetchRequest = Cheat.fetchRequest() as NSFetchRequest<Cheat>
|
||||
cheatsFetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(Cheat.game), self.game)
|
||||
cheatsFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Cheat.name, ascending: true)]
|
||||
|
||||
let cheatsDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: cheatsFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
cheatsDataSource.cellConfigurationHandler = { (cell, cheat, indexPath) in
|
||||
cell.textLabel?.text = cheat.name
|
||||
|
||||
configure(cell, recordedObject: cheat)
|
||||
}
|
||||
|
||||
let dataSources = [gameDataSource, saveStatesDataSource, cheatsDataSource] as! [RSTArrayTableViewDataSource<NSManagedObject>]
|
||||
|
||||
let dataSource = RSTCompositeTableViewDataSource(dataSources: dataSources)
|
||||
dataSource.proxy = self
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func fetchRecords()
|
||||
{
|
||||
var recordsByObjectURI = [URL: Record<NSManagedObject>]()
|
||||
|
||||
do
|
||||
{
|
||||
let recordedObjects = ([self.game!] + Array(self.game.saveStates) + Array(self.game.cheats)) as! [SyncableManagedObject]
|
||||
let records = try SyncManager.shared.recordController.fetchRecords(for: recordedObjects)
|
||||
|
||||
for record in records
|
||||
{
|
||||
guard let recordedObject = record.recordedObject else { continue }
|
||||
|
||||
recordsByObjectURI[recordedObject.objectID.uriRepresentation()] = record
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
self.recordsByObjectURI = recordsByObjectURI
|
||||
}
|
||||
}
|
||||
|
||||
extension GameSyncStatusViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
guard self.dataSource.tableView(self.tableView, numberOfRowsInSection: section) > 0 else { return nil }
|
||||
|
||||
switch Section.allCases[section]
|
||||
{
|
||||
case .game: return NSLocalizedString("Game", comment: "")
|
||||
case .saveStates: return NSLocalizedString("Save States", comment: "")
|
||||
case .cheats: return NSLocalizedString("Cheats", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,18 @@ class SyncStatusViewController: UITableViewController
|
||||
|
||||
self.fetchConflictedRecords()
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "showGame" else { return }
|
||||
|
||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
||||
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
|
||||
let gameSyncStatusViewController = segue.destination as! GameSyncStatusViewController
|
||||
gameSyncStatusViewController.game = game
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyncStatusViewController
|
||||
@ -92,20 +104,21 @@ private extension SyncStatusViewController
|
||||
for record in records
|
||||
{
|
||||
guard let recordedObject = record.recordedObject else { continue }
|
||||
|
||||
let conflictedGame: Game?
|
||||
|
||||
switch recordedObject
|
||||
{
|
||||
case let game as Game: conflictedGame = game
|
||||
case let saveState as SaveState: conflictedGame = saveState.game
|
||||
case let cheat as Cheat: conflictedGame = cheat.game
|
||||
default: conflictedGame = nil
|
||||
recordedObject.managedObjectContext?.performAndWait {
|
||||
let conflictedGame: Game?
|
||||
|
||||
switch recordedObject
|
||||
{
|
||||
case let game as Game: conflictedGame = game
|
||||
case let saveState as SaveState: conflictedGame = saveState.game
|
||||
case let cheat as Cheat: conflictedGame = cheat.game
|
||||
default: conflictedGame = nil
|
||||
}
|
||||
|
||||
guard let game = conflictedGame else { return }
|
||||
|
||||
gameConflictsCount[game.objectID.uriRepresentation(), default: 0] += 1
|
||||
}
|
||||
|
||||
guard let game = conflictedGame else { continue }
|
||||
|
||||
gameConflictsCount[game.objectID.uriRepresentation(), default: 0] += 1
|
||||
}
|
||||
|
||||
self.gameConflictsCount = gameConflictsCount
|
||||
|
||||
2
External/Harmony
vendored
2
External/Harmony
vendored
@ -1 +1 @@
|
||||
Subproject commit 6f5dc280077cee8382ccc6622ee8383aadb59d48
|
||||
Subproject commit ef2c685333abe1cce9ee0a43150656634ab06081
|
||||
Loading…
Reference in New Issue
Block a user