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

View File

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

View File

@@ -32,5 +32,7 @@ extension Game {
@NSManaged public var cover_icloud_path : String? @NSManaged public var cover_icloud_path : String?
@NSManaged public var console: Console? @NSManaged public var console: Console?
@NSManaged public var series: GameSeries? @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.cover_icloud_path = game.cover_icloud_path
cdGame.pickupDescription = game.pickupDescription cdGame.pickupDescription = game.pickupDescription
cdGame.isDigital = game.isDigital 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) { if let date = Date.from(string: game.createdAt) {
cdGame.createdAt = date cdGame.createdAt = date
@@ -216,6 +218,8 @@ struct BHLGame : Decodable {
let publisher : String? let publisher : String?
let console : UUID let console : UUID
let cover_icloud_path : String? let cover_icloud_path : String?
let playtime_h : Int?
let playtime_min : Int?
} }
struct BHLAccessory : Decodable { 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 { struct GameDetailView : View {
@ObservedObject var game : Game @ObservedObject var game : Game
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode> @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@@ -50,17 +78,6 @@ struct GameDetailView : View {
let defaultImage = UIImage() 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 @State var isLent_raw : Bool = false
private var isLent: Binding<Bool> { private var isLent: Binding<Bool> {
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> { var finishedDateBinding: Binding<Date> {
Binding<Date>( Binding<Date>(
get: { self.game.finishedDate ?? Date() }, get: { self.game.finishedDate ?? Date() },
set: { self.game.finishedDate = $0 }) 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 { var GameIsFinished : some View {
Group { Group {
Toggle(isOn: $game.isFinished , label: { Toggle(isOn: $game.isFinished , label: {
Text("Durchgezockt") Text("Durchgezockt")
}) }).onChange(of: game.isFinished) {
if !$0 {
game.playtime_h = 0
game.playtime_min = 0
}
}
if game.isFinished { 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: { Toggle(isOn: hasFinishedDate, label: {
Text("Gibts ein Datum") Text("Gibts ein Datum")
}) })
@@ -105,12 +161,24 @@ struct GameDetailView : View {
TextField("Videogame name", text: $game.name) TextField("Videogame name", text: $game.name)
//Gray color should indicate immutable data //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: { Toggle(isOn: $game.isDigital, label: {
Text("Nur Digital") Text("Nur Digital")
}) })
Toggle(isOn: $game.inWishlist, label: {
Text("In Wunschliste")
})
}
Section(header: Text("Details")) {
DatePicker("In Sammlung seit", DatePicker("In Sammlung seit",
selection: $game.createdAt, selection: $game.createdAt,
in: ...Date(), in: ...Date(),
@@ -118,39 +186,32 @@ struct GameDetailView : View {
HStack { HStack {
Text("Anlass") Text("Anlass")
TextField("Anlass", text: $game.pickupDescription ?? "") TextEditor(text: pickupDscriptionBinding).frame(height: 100)
} }
Toggle(isOn: $game.inWishlist, label: { GameSeriesPicker(game: game)
Text("In Wunschliste")
})
Toggle(isOn: isLent, label: { Toggle(isOn: isLent, label: {
Text("Verliehen?") Text("Verliehen?")
}) })
if isLent.wrappedValue { if isLent.wrappedValue {
TextField("Verliehen an", text: $game.lentTo ?? "") TextField("Verliehen an", text: lentToBinding)
} }
GameSeriesPicker(game: game)
GameIsFinished GameIsFinished
} }
Section { Section(header: Text("Notizen")) {
VStack{ TextEditor(text: notesBinding).frame(height: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/)
Button("Neues Cover auswählen") { }
Section(header: Text("Anhänge")){
List {
AttachmentCellImage(game: game) {
self.isImportingCover = true 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( .fileImporter(
isPresented: $isImportingCover, isPresented: $isImportingCover,
allowedContentTypes: [.jpeg, .png], allowedContentTypes: [.jpeg, .png],
@@ -174,8 +235,11 @@ struct GameDetailView : View {
print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")") print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")")
}else{ }else{
Alert(title: Text("Falscher Ordner")) Alert(
print("Außerhalb \(selectedFile.relativeString)") title: Text("Falscher Ordner"),
message: Text("Bitte nutze nur Dateien aus dem Zockerhöhle iCloud Ordner"),
dismissButton: .default(Text("Alles klar!"))
)
} }
}catch{ }catch{
print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'") print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'")
@@ -196,10 +260,13 @@ struct GameDetailView : View {
}) })
.accentColor(.red) .accentColor(.red)
.alert(isPresented: $showDeleteAlert, content: { .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: { 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)
}
//TZOOOODOOOOOO
//self.gameVM.removeGame()
}), secondaryButton: Alert.Button.cancel(Text("Lieber doch nicht"))) }), secondaryButton: Alert.Button.cancel(Text("Lieber doch nicht")))
}) })
} }

View File

@@ -32,6 +32,8 @@
<attribute name="name" attributeType="String" defaultValueString=""/> <attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="notes" optional="YES" attributeType="String"/> <attribute name="notes" optional="YES" attributeType="String"/>
<attribute name="pickupDescription" 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="publisher" optional="YES" attributeType="String"/>
<attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/> <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"/> <relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="games" inverseEntity="Console"/>
@@ -52,7 +54,7 @@
<elements> <elements>
<element name="Accessory" positionX="-265.9140625" positionY="29.15625" width="128" height="149"/> <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="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="GameSeries" positionX="-686.828125" positionY="359.20703125" width="128" height="89"/>
<element name="GameSeriesCover" positionX="-477" positionY="180" width="128" height="44"/> <element name="GameSeriesCover" positionX="-477" positionY="180" width="128" height="44"/>
</elements> </elements>