Added playtime in Game Detail

This commit is contained in:
2021-05-26 14:58:52 +02:00
parent 41b304f655
commit dc4f8b0ebe
6 changed files with 131 additions and 52 deletions

View File

@@ -152,6 +152,7 @@
B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */,
B93D60CD22D88F5700DD390F /* AccessoryDetailView.swift */,
B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */,
B9ED3DDA265D47EB00FD2D46 /* GameView.swift */,
B94112E3233B597D00159AE4 /* ConsoleEditView.swift */,
B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */,
B9E2A07C233B6E4F00EAEB14 /* ConsoleAllView.swift */,
@@ -159,7 +160,6 @@
B9A0550222F8C2740054D9A0 /* MainView.swift */,
B9EC0986238555BF004BC9AB /* SettingsView.swift */,
B9839982233A086A002F9946 /* Overview.swift */,
B9ED3DDA265D47EB00FD2D46 /* GameView.swift */,
);
path = Views;
sourceTree = "<group>";

View File

@@ -13,17 +13,17 @@ import SwiftUI
@objc(Game)
public class Game: NSManagedObject, Identifiable {
public func addGameSeries(by objectIDStringified : String) {
if let url = URL(string: objectIDStringified) {
let persistentStoreCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
if let objectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: url) {
if let gameSeries = CDManager.shared.viewContext.object(with: objectID) as? GameSeries {
self.series = gameSeries
}
}
}
}
// public func addGameSeries(by objectIDStringified : String) {
// if let url = URL(string: objectIDStringified) {
// let persistentStoreCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
//
// if let objectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: url) {
// if let gameSeries = CDManager.shared.viewContext.object(with: objectID) as? GameSeries {
// self.series = gameSeries
// }
// }
// }
// }
public static func compareByCreationDate(gameA : Game, gameB : Game) -> Bool {
return gameA.createdAt < gameB.createdAt
@@ -54,6 +54,8 @@ extension Game : Encodable {
case console
case cover_icloud_path
case pickupDescription
case playtime_h
case playtime_min
}
public func encode(to encoder: Encoder) throws {
@@ -71,6 +73,8 @@ extension Game : Encodable {
try container.encode(finishedDate, forKey: .finishedDate)
try container.encode(inWishlist, forKey: .inWishlist)
try container.encode(cover_icloud_path ?? "", forKey: .cover_icloud_path)
try container.encode(playtime_h, forKey: .playtime_h)
try container.encode(playtime_min, forKey: .playtime_min)
let consoleUUID : String = console?.uuid.uuidString ?? ""
try container.encode(consoleUUID, forKey: .console)

View File

@@ -32,5 +32,7 @@ extension Game {
@NSManaged public var cover_icloud_path : String?
@NSManaged public var console: Console?
@NSManaged public var series: GameSeries?
@NSManaged public var playtime_h : Int
@NSManaged public var playtime_min : Int
}

View File

@@ -86,6 +86,8 @@ class LibraryImport {
cdGame.cover_icloud_path = game.cover_icloud_path
cdGame.pickupDescription = game.pickupDescription
cdGame.isDigital = game.isDigital
cdGame.playtime_h = game.playtime_h ?? 0
cdGame.playtime_min = game.playtime_min ?? 0
if let date = Date.from(string: game.createdAt) {
cdGame.createdAt = date
@@ -216,6 +218,8 @@ struct BHLGame : Decodable {
let publisher : String?
let console : UUID
let cover_icloud_path : String?
let playtime_h : Int?
let playtime_min : Int?
}
struct BHLAccessory : Decodable {

View File

@@ -40,6 +40,34 @@ struct GameSeriesPicker: View {
}
}
struct AttachmentCellImage : View {
@ObservedObject var game : Game
var onClick: ()->()
let defaultImage = UIImage()
var body: some View {
HStack{
Button("Cover") {
self.onClick()
}
Spacer()
Group {
Image(uiImage: ICloudManager.imageFrom(path: game.cover_icloud_path) ?? defaultImage)
.resizable()
.scaledToFit()
}.frame(width:100, height: 100)
}
}
init(game: Game, onClick: @escaping ()->()) {
self.game = game
self.onClick = onClick
}
}
struct GameDetailView : View {
@ObservedObject var game : Game
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@@ -49,18 +77,7 @@ struct GameDetailView : View {
@State var isImportingCover : Bool = false
let defaultImage = UIImage()
@State var hasFinishedDate_raw : Bool = false
private var hasFinishedDate: Binding<Bool> {
Binding<Bool>(
get: { self.hasFinishedDate_raw || self.game.finishedDate != .none },
set: {
hasFinishedDate_raw = $0
if !$0 { self.game.finishedDate = .none }
}
)
}
@State var isLent_raw : Bool = false
private var isLent: Binding<Bool> {
Binding<Bool>(
@@ -72,19 +89,58 @@ struct GameDetailView : View {
)
}
@State var hasFinishedDate_raw : Bool = false
private var hasFinishedDate: Binding<Bool> {
Binding<Bool>(
get: { self.hasFinishedDate_raw || self.game.finishedDate != .none },
set: {
hasFinishedDate_raw = $0
if !$0 { self.game.finishedDate = .none }
}
)
}
var finishedDateBinding: Binding<Date> {
Binding<Date>(
get: { self.game.finishedDate ?? Date() },
set: { self.game.finishedDate = $0 })
}
var notesBinding: Binding<String> {
Binding<String>(
get: { self.game.notes ?? "" },
set: { self.game.notes = $0 })
}
var pickupDscriptionBinding: Binding<String> {
Binding<String>(
get: { self.game.pickupDescription ?? "" },
set: { self.game.pickupDescription = $0 })
}
var lentToBinding: Binding<String> {
Binding<String>(
get: { self.game.lentTo ?? "" },
set: { self.game.lentTo = $0 })
}
var GameIsFinished : some View {
Group {
Toggle(isOn: $game.isFinished , label: {
Text("Durchgezockt")
})
}).onChange(of: game.isFinished) {
if !$0 {
game.playtime_h = 0
game.playtime_min = 0
}
}
if game.isFinished {
HStack {
Stepper("\(game.playtime_h)h", value: $game.playtime_h, in: 0...200)
Stepper("\(game.playtime_min)min", value: $game.playtime_min, in: 0...60)
}
Toggle(isOn: hasFinishedDate, label: {
Text("Gibts ein Datum")
})
@@ -105,12 +161,24 @@ struct GameDetailView : View {
TextField("Videogame name", text: $game.name)
//Gray color should indicate immutable data
Text("\(game.console?.name ?? "No console")").foregroundColor(.gray)
HStack {
Text("Konsole")
Text("\(game.console?.name ?? "No console")").foregroundColor(.gray)
}
Toggle(isOn: $game.isDigital, label: {
Text("Nur Digital")
})
Toggle(isOn: $game.inWishlist, label: {
Text("In Wunschliste")
})
}
Section(header: Text("Details")) {
DatePicker("In Sammlung seit",
selection: $game.createdAt,
in: ...Date(),
@@ -118,39 +186,32 @@ struct GameDetailView : View {
HStack {
Text("Anlass")
TextField("Anlass", text: $game.pickupDescription ?? "")
TextEditor(text: pickupDscriptionBinding).frame(height: 100)
}
Toggle(isOn: $game.inWishlist, label: {
Text("In Wunschliste")
})
GameSeriesPicker(game: game)
Toggle(isOn: isLent, label: {
Text("Verliehen?")
})
if isLent.wrappedValue {
TextField("Verliehen an", text: $game.lentTo ?? "")
TextField("Verliehen an", text: lentToBinding)
}
GameSeriesPicker(game: game)
GameIsFinished
}
Section {
VStack{
Button("Neues Cover auswählen") {
Section(header: Text("Notizen")) {
TextEditor(text: notesBinding).frame(height: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/)
}
Section(header: Text("Anhänge")){
List {
AttachmentCellImage(game: game) {
self.isImportingCover = true
}
Group {
Image(uiImage: ICloudManager.imageFrom(path: game.cover_icloud_path) ?? defaultImage)
.resizable()
.scaledToFit()
}.frame(width:100, height: 100)
}
}
@@ -159,7 +220,7 @@ struct GameDetailView : View {
}
}
.navigationBarTitle(Text("\(game.name ?? "N/A")"), displayMode: .automatic)
.navigationBarTitle(Text("\(game.name)"), displayMode: .automatic)
.fileImporter(
isPresented: $isImportingCover,
allowedContentTypes: [.jpeg, .png],
@@ -174,8 +235,11 @@ struct GameDetailView : View {
print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")")
}else{
Alert(title: Text("Falscher Ordner"))
print("Außerhalb \(selectedFile.relativeString)")
Alert(
title: Text("Falscher Ordner"),
message: Text("Bitte nutze nur Dateien aus dem Zockerhöhle iCloud Ordner"),
dismissButton: .default(Text("Alles klar!"))
)
}
}catch{
print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'")
@@ -196,10 +260,13 @@ struct GameDetailView : View {
})
.accentColor(.red)
.alert(isPresented: $showDeleteAlert, content: {
Alert(title: Text("Aus Zockerhöhle entfernen"), message: Text("Willst du '\(self.game.name ?? "n/a")' wirklich aus der Zockerhöhle werfen?"), primaryButton: Alert.Button.destructive(Text("Ja!"), action: {
//TZOOOODOOOOOO
//self.gameVM.removeGame()
Alert(title: Text("Aus Zockerhöhle entfernen"), message: Text("Willst du '\(self.game.name)' wirklich aus der Zockerhöhle werfen?"), primaryButton: Alert.Button.destructive(Text("Ja!"), action: {
//
self.presentationMode.wrappedValue.dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
CDManager.shared.viewContext.delete(game)
}
}), secondaryButton: Alert.Button.cancel(Text("Lieber doch nicht")))
})
}

View File

@@ -32,6 +32,8 @@
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="notes" optional="YES" attributeType="String"/>
<attribute name="pickupDescription" optional="YES" attributeType="String"/>
<attribute name="playtime_h" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="playtime_min" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="publisher" optional="YES" attributeType="String"/>
<attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/>
<relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="games" inverseEntity="Console"/>
@@ -52,7 +54,7 @@
<elements>
<element name="Accessory" positionX="-265.9140625" positionY="29.15625" width="128" height="149"/>
<element name="Console" positionX="-535.7890625" positionY="56.03515625" width="128" height="164"/>
<element name="Game" positionX="-288.828125" positionY="276.7421875" width="128" height="260"/>
<element name="Game" positionX="-288.828125" positionY="276.7421875" width="128" height="290"/>
<element name="GameSeries" positionX="-686.828125" positionY="359.20703125" width="128" height="89"/>
<element name="GameSeriesCover" positionX="-477" positionY="180" width="128" height="44"/>
</elements>