Added playtime in Game Detail
This commit is contained in:
@@ -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>";
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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")))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user