diff --git a/Zockerhoehle.xcodeproj/project.pbxproj b/Zockerhoehle.xcodeproj/project.pbxproj index dd0ce9f..c80780f 100644 --- a/Zockerhoehle.xcodeproj/project.pbxproj +++ b/Zockerhoehle.xcodeproj/project.pbxproj @@ -19,6 +19,9 @@ B94CB50422D1352F0029BFAD /* Game+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F822D1352F0029BFAD /* Game+CoreDataProperties.swift */; }; B94CB53722D3B3CC0029BFAD /* GameDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */; }; B952648526602CB600BB4324 /* MainView_Zockerhoehle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B952648426602CB600BB4324 /* MainView_Zockerhoehle.swift */; }; + B95264872661041D00BB4324 /* ConsoleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B95264862661041D00BB4324 /* ConsoleView.swift */; }; + B95264A826610E0800BB4324 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B95264A726610E0800BB4324 /* Preview Assets.xcassets */; }; + B969C5F0266693EE00401B89 /* GameEditMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B969C5EF266693EE00401B89 /* GameEditMode.swift */; }; B9839983233A086A002F9946 /* Overview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9839982233A086A002F9946 /* Overview.swift */; }; B98A734D22BAD27D00FB3410 /* Zockerhoehle.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B98A731722BA9E4600FB3410 /* Zockerhoehle.xcdatamodeld */; }; B98A736022C1738800FB3410 /* CDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98A735F22C1738800FB3410 /* CDManager.swift */; }; @@ -40,7 +43,7 @@ B9EC0987238555BF004BC9AB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EC0986238555BF004BC9AB /* SettingsView.swift */; }; B9ED3DD7265534C000FD2D46 /* test_date_utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9ED3DD6265534C000FD2D46 /* test_date_utils.swift */; }; B9ED3DD9265D1E5600FD2D46 /* CDPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9ED3DD8265D1E5600FD2D46 /* CDPreview.swift */; }; - B9ED3DDB265D47EC00FD2D46 /* GameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9ED3DDA265D47EB00FD2D46 /* GameView.swift */; }; + B9ED3DDB265D47EC00FD2D46 /* GameCover.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9ED3DDA265D47EB00FD2D46 /* GameCover.swift */; }; B9F44ABA22F312E600FC6B29 /* ConsoleLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */; }; /* End PBXBuildFile section */ @@ -84,6 +87,9 @@ B94CB4FA22D1352F0029BFAD /* Console+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Console+CoreDataProperties.swift"; sourceTree = ""; }; B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameDetailView.swift; sourceTree = ""; }; B952648426602CB600BB4324 /* MainView_Zockerhoehle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView_Zockerhoehle.swift; sourceTree = ""; }; + B95264862661041D00BB4324 /* ConsoleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleView.swift; sourceTree = ""; }; + B95264A726610E0800BB4324 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + B969C5EF266693EE00401B89 /* GameEditMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameEditMode.swift; sourceTree = ""; }; B9839982233A086A002F9946 /* Overview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overview.swift; sourceTree = ""; }; B98A731822BA9E4600FB3410 /* Zockerhoehle.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Zockerhoehle.xcdatamodel; sourceTree = ""; }; B98A735F22C1738800FB3410 /* CDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CDManager.swift; sourceTree = ""; }; @@ -106,7 +112,7 @@ B9EC0986238555BF004BC9AB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; B9ED3DD6265534C000FD2D46 /* test_date_utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test_date_utils.swift; sourceTree = ""; }; B9ED3DD8265D1E5600FD2D46 /* CDPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CDPreview.swift; sourceTree = ""; }; - B9ED3DDA265D47EB00FD2D46 /* GameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameView.swift; sourceTree = ""; }; + B9ED3DDA265D47EB00FD2D46 /* GameCover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameCover.swift; sourceTree = ""; }; B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLibraryView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -146,13 +152,15 @@ B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */, B93D60CD22D88F5700DD390F /* AccessoryDetailView.swift */, B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */, - B9ED3DDA265D47EB00FD2D46 /* GameView.swift */, + B969C5EF266693EE00401B89 /* GameEditMode.swift */, + B9ED3DDA265D47EB00FD2D46 /* GameCover.swift */, B94112E3233B597D00159AE4 /* ConsoleEditView.swift */, B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */, B9E2A07C233B6E4F00EAEB14 /* ConsoleAllView.swift */, B94112DF233A4EF800159AE4 /* GamePickupsView.swift */, B9EC0986238555BF004BC9AB /* SettingsView.swift */, B9839982233A086A002F9946 /* Overview.swift */, + B95264862661041D00BB4324 /* ConsoleView.swift */, ); path = Views; sourceTree = ""; @@ -202,10 +210,19 @@ B93C1BA321496BFE0014FD6E /* Assets.xcassets */, B93C1BA821496BFE0014FD6E /* Info.plist */, B98A731722BA9E4600FB3410 /* Zockerhoehle.xcdatamodeld */, + B95264A626610E0800BB4324 /* Preview Content */, ); path = Zockerhoehle; sourceTree = ""; }; + B95264A626610E0800BB4324 /* Preview Content */ = { + isa = PBXGroup; + children = ( + B95264A726610E0800BB4324 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; B98A734622BACA9C00FB3410 /* CDModel */ = { isa = PBXGroup; children = ( @@ -323,6 +340,7 @@ buildActionMask = 2147483647; files = ( B9BCF4CA2168ACB600ECBAAC /* LaunchScreen.storyboard in Resources */, + B95264A826610E0800BB4324 /* Preview Assets.xcassets in Resources */, B93C1BA421496BFE0014FD6E /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -378,6 +396,7 @@ B952648526602CB600BB4324 /* MainView_Zockerhoehle.swift in Sources */, B98CBBDC264E98DE00B1B7AC /* GameSeries+CoreDataClass.swift in Sources */, B9A0550122F8C22D0054D9A0 /* GameSeriesAllView.swift in Sources */, + B969C5F0266693EE00401B89 /* GameEditMode.swift in Sources */, B94112E4233B597D00159AE4 /* ConsoleEditView.swift in Sources */, B94112DE233A37DD00159AE4 /* DateConversion.swift in Sources */, B93D60CE22D88F5700DD390F /* AccessoryDetailView.swift in Sources */, @@ -387,9 +406,10 @@ B94CB50422D1352F0029BFAD /* Game+CoreDataProperties.swift in Sources */, B98A736022C1738800FB3410 /* CDManager.swift in Sources */, B9E2A07E233B6E4F00EAEB14 /* ConsoleAllView.swift in Sources */, + B95264872661041D00BB4324 /* ConsoleView.swift in Sources */, B9BCCEB92653BDEA005F46D6 /* ICloudManager.swift in Sources */, B9EC0987238555BF004BC9AB /* SettingsView.swift in Sources */, - B9ED3DDB265D47EC00FD2D46 /* GameView.swift in Sources */, + B9ED3DDB265D47EC00FD2D46 /* GameCover.swift in Sources */, B9ED3DD9265D1E5600FD2D46 /* CDPreview.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -539,6 +559,7 @@ CODE_SIGN_ENTITLEMENTS = Zockerhoehle/Zockerhoehle.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "Zockerhoehle/Preview\\ Content"; DEVELOPMENT_TEAM = 85J8CBD673; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Zockerhoehle/Info.plist; @@ -550,6 +571,8 @@ PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.zockerhoehle; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -562,6 +585,7 @@ CODE_SIGN_ENTITLEMENTS = Zockerhoehle/Zockerhoehle.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "Zockerhoehle/Preview\\ Content"; DEVELOPMENT_TEAM = M9N7K3KZX9; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Zockerhoehle/Info.plist; @@ -573,6 +597,8 @@ PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.zockerhoehle; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Zockerhoehle/CDModel/Console+CoreDataClass.swift b/Zockerhoehle/CDModel/Console+CoreDataClass.swift index 866cc77..00f1d4a 100644 --- a/Zockerhoehle/CDModel/Console+CoreDataClass.swift +++ b/Zockerhoehle/CDModel/Console+CoreDataClass.swift @@ -22,7 +22,7 @@ public class Console: NSManagedObject, Identifiable { Game.compareByCreationDate(gameA: $0, gameB: $1) }) else { return false } - return newestGameConsoleA.createdAt > newestGameConsoleB.createdAt + return newestGameConsoleA.pickupOrReleaseDate > newestGameConsoleB.pickupOrReleaseDate } // Defining default values during creation diff --git a/Zockerhoehle/CDModel/Game+CoreDataClass.swift b/Zockerhoehle/CDModel/Game+CoreDataClass.swift index 93e7761..fb21205 100644 --- a/Zockerhoehle/CDModel/Game+CoreDataClass.swift +++ b/Zockerhoehle/CDModel/Game+CoreDataClass.swift @@ -15,14 +15,14 @@ import SwiftUI public class Game: NSManagedObject, Identifiable { public static func compareByCreationDate(gameA : Game, gameB : Game) -> Bool { - return gameA.createdAt < gameB.createdAt + return gameA.pickupOrReleaseDate < gameB.pickupOrReleaseDate } @objc private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) { super.init(entity: entity, insertInto: context) - self.createdAt = Date() + self.pickupOrReleaseDate = Date() self.uuid = UUID() print("Set UUID to \(self.uuid)") } @@ -35,7 +35,8 @@ extension Game : Encodable { case notes case isDigital case lentTo - case createdAt + case pickupOrReleaseDate + case isPickupDate case publisher case isFinished case finishedDate @@ -55,7 +56,8 @@ extension Game : Encodable { try container.encode(notes ?? "", forKey: .notes) try container.encode(isDigital, forKey: .isDigital) try container.encode(lentTo ?? "", forKey: .lentTo) - try container.encode(createdAt.formattedInTimeZone(), forKey: .createdAt) + try container.encode(pickupOrReleaseDate.formattedInTimeZone(), forKey: .pickupOrReleaseDate) + try container.encode(isPickupDate, forKey: .isPickupDate) try container.encode(pickupDescription ?? "", forKey: .pickupDescription) try container.encode(publisher ?? "", forKey: .publisher) try container.encode(isFinished, forKey: .isFinished) diff --git a/Zockerhoehle/CDModel/Game+CoreDataProperties.swift b/Zockerhoehle/CDModel/Game+CoreDataProperties.swift index a5ff1ad..bb86774 100644 --- a/Zockerhoehle/CDModel/Game+CoreDataProperties.swift +++ b/Zockerhoehle/CDModel/Game+CoreDataProperties.swift @@ -18,7 +18,8 @@ extension Game { } @NSManaged public var circumstances: String? - @NSManaged public var createdAt: Date + @NSManaged public var isPickupDate : Bool + @NSManaged public var pickupOrReleaseDate: Date @NSManaged public var pickupDescription : String? @NSManaged public var inWishlist: Bool @NSManaged public var isDigital: Bool diff --git a/Zockerhoehle/CDPreview.swift b/Zockerhoehle/CDPreview.swift index 5bd3395..5aebe53 100644 --- a/Zockerhoehle/CDPreview.swift +++ b/Zockerhoehle/CDPreview.swift @@ -32,4 +32,34 @@ struct CDPreview { } } + private func games(predicate: NSPredicate?) -> [Game] { + let gameFR = NSFetchRequest(entityName: "Game") + if let predicate = predicate { + gameFR.predicate = predicate + } + do { + let games : [Game] = try CDManager.preview.viewContextPreview.fetch(gameFR) + + return games + }catch { + return [] + } + } + + func games(console: Console) -> [Game] { + return games(predicate: NSPredicate(format: "console = %@", console)) + } + + func games() -> [Game] { + return games(predicate: .none) + } + + func game(name: String) -> Game? { + return self.games().first(where: {$0.name == name}) + } + + func console(name: String) -> Console? { + return self.consoles().first(where: {$0.name == name}) + } + } diff --git a/Zockerhoehle/ICloudManager.swift b/Zockerhoehle/ICloudManager.swift index 2c15fe5..5b43942 100644 --- a/Zockerhoehle/ICloudManager.swift +++ b/Zockerhoehle/ICloudManager.swift @@ -38,6 +38,13 @@ class ICloudManager { return FileManager.default.fileExists(atPath: url.path) } + #if DEBUG + let asset_name = path.replacingOccurrences(of: "/", with: "-") + if UIImage(named: asset_name) != nil { + return true + } + #endif + return false; } @@ -45,13 +52,31 @@ class ICloudManager { guard let path = path else { return .none } do { - let url = documents_folder?.appendingPathComponent(path) - let imageData = try Data(contentsOf: url!) - - return UIImage(data: imageData)! + if let url = documents_folder?.appendingPathComponent(path) { + let imageData = try Data(contentsOf: url) + + return UIImage(data:imageData) + } }catch { return .none } + + #if DEBUG + let asset_name = path.replacingOccurrences(of: "/", with: "-") + if let image = UIImage(named: asset_name) { + return image + } + #endif + + return .none + } + + static public func imageRatio(path: String?) -> CGFloat { + if let image = ICloudManager.imageFrom(path: path) { + return image.size.width / image.size.height + } + + return 1 } static public func inICloudContainer(url : URL?) -> Bool { diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/Contents.json new file mode 100644 index 0000000..0599e4a --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "dreamcast.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/dreamcast.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/dreamcast.png new file mode 100644 index 0000000..bdcdb15 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-dreamcast.png.imageset/dreamcast.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/Contents.json new file mode 100644 index 0000000..feb10eb --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "gamecube.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/gamecube.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/gamecube.png new file mode 100644 index 0000000..83c87da Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-gamecube.png.imageset/gamecube.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/Contents.json new file mode 100644 index 0000000..4aae704 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "nes.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/nes.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/nes.png new file mode 100644 index 0000000..8df421c Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nes.png.imageset/nes.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/Contents.json new file mode 100644 index 0000000..8872c80 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "nintendo_switch.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/nintendo_switch.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/nintendo_switch.png new file mode 100644 index 0000000..3258cb0 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-nintendo_switch.png.imageset/nintendo_switch.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/Contents.json new file mode 100644 index 0000000..0848d81 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ps2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/ps2.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/ps2.png new file mode 100644 index 0000000..eedca92 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps2.png.imageset/ps2.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/Contents.json new file mode 100644 index 0000000..731e1ae --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ps3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/ps3.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/ps3.png new file mode 100644 index 0000000..d1e041a Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps3.png.imageset/ps3.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/Contents.json new file mode 100644 index 0000000..dd17580 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ps5.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/ps5.png b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/ps5.png new file mode 100644 index 0000000..f92daf9 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-ps5.png.imageset/ps5.png differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/Contents.json new file mode 100644 index 0000000..9961ab9 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "xbox_one.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/xbox_one.jpg b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/xbox_one.jpg new file mode 100644 index 0000000..31a8684 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/consoles-xbox_one.jpg.imageset/xbox_one.jpg differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/Contents.json new file mode 100644 index 0000000..c4fb448 --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ocarina_of_time.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/ocarina_of_time.jpg b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/ocarina_of_time.jpg new file mode 100644 index 0000000..1a7f687 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-n64-ocarina_of_time.jpg.imageset/ocarina_of_time.jpg differ diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/Contents.json b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/Contents.json new file mode 100644 index 0000000..8de5a3a --- /dev/null +++ b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "zelda_breath_of_the_wild.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/zelda_breath_of_the_wild.jpg b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/zelda_breath_of_the_wild.jpg new file mode 100644 index 0000000..8e4dc23 Binary files /dev/null and b/Zockerhoehle/Preview Content/Preview Assets.xcassets/games-switch-zelda_breath_of_the_wild.jpg.imageset/zelda_breath_of_the_wild.jpg differ diff --git a/Zockerhoehle/Utils/DateConversion.swift b/Zockerhoehle/Utils/DateConversion.swift index 74ad928..80fe128 100644 --- a/Zockerhoehle/Utils/DateConversion.swift +++ b/Zockerhoehle/Utils/DateConversion.swift @@ -9,8 +9,11 @@ import Foundation extension Date { - static let zockerhoehle_date_format = "yyyy-MM-dd'T'HH:mm:ssZ" - func formattedInTimeZone(timezone : TimeZone = .current) -> String { + static let export_date_format = "yyyy-MM-dd'T'HH:mm:ssZ" + + static let view_date_format = "dd.MM.yyyy" + + func formattedInTimeZone(timezone : TimeZone = .current, dateFormat : String = Date.export_date_format) -> String { // 1) Create a DateFormatter() object. let format = DateFormatter() @@ -18,7 +21,7 @@ extension Date { format.timeZone = timezone // 3) Set the format of the altered date. - format.dateFormat = Date.zockerhoehle_date_format + format.dateFormat = dateFormat // 4) Set the current date, altered by timezone. let dateString = format.string(from: self) @@ -29,7 +32,7 @@ extension Date { static func from(string : String, timezone : TimeZone = .current) -> Date? { let format = DateFormatter() format.timeZone = timezone - format.dateFormat = Date.zockerhoehle_date_format + format.dateFormat = Date.export_date_format return format.date(from: string) } diff --git a/Zockerhoehle/Utils/LibraryImport.swift b/Zockerhoehle/Utils/LibraryImport.swift index 44e030b..2febf70 100644 --- a/Zockerhoehle/Utils/LibraryImport.swift +++ b/Zockerhoehle/Utils/LibraryImport.swift @@ -92,11 +92,13 @@ class LibraryImport { cdGame.isDigital = game.isDigital cdGame.playtime_h = game.playtime_h ?? 0 cdGame.playtime_min = game.playtime_min ?? 0 + cdGame.isPickupDate = game.isPickupDate - if let date = Date.from(string: game.createdAt) { - cdGame.createdAt = date + if let date = Date.from(string: game.pickupOrReleaseDate) { + cdGame.pickupOrReleaseDate = date + }else{ - print("Could not decode date '\(game.createdAt)' for game '\(cdGame.name)'") + print("Could not decode date '\(game.pickupOrReleaseDate)' for game '\(cdGame.name)'") } cdConsole.addToGames(cdGame) @@ -218,7 +220,8 @@ struct BHLGame : Decodable { let isFinished : Bool let finishedDate : Date? let notes : String? - let createdAt : String + let pickupOrReleaseDate : String + let isPickupDate : Bool = false let pickupDescription : String? let publisher : String? let console : UUID diff --git a/Zockerhoehle/Views/ConsoleLibraryView.swift b/Zockerhoehle/Views/ConsoleLibraryView.swift index a009fb9..f7b1d88 100644 --- a/Zockerhoehle/Views/ConsoleLibraryView.swift +++ b/Zockerhoehle/Views/ConsoleLibraryView.swift @@ -116,7 +116,7 @@ struct ConsoleLibraryView : View { LazyVGrid(columns: columns, spacing: 20) { ForEach(games.filter({$0.inWishlist == self.showWishlist})) { game in NavigationLink(destination: GameDetailView(game: game)) { - GameView(game: game) + GameCover(game: game) } } }.padding() diff --git a/Zockerhoehle/Views/ConsoleView.swift b/Zockerhoehle/Views/ConsoleView.swift new file mode 100644 index 0000000..2b1704f --- /dev/null +++ b/Zockerhoehle/Views/ConsoleView.swift @@ -0,0 +1,54 @@ +// +// File.swift +// Zockerhoehle +// +// Created by Julian-Steffen Müller on 28.05.21. +// Copyright © 2021 Julian-Steffen Müller. All rights reserved. +// + +import Foundation +import SwiftUI + +struct ConsoleView : View { + @ObservedObject var console : Console + + public var defaultImage = UIImage() + + var body: some View { + ZStack { + Rectangle() + .fill(Color(red: 1, green: 0, blue: 0.03)) + + + Image(uiImage: ICloudManager.imageFrom(path: console.logo_icloud_path) ?? defaultImage) + .resizable() + .padding() + .scaledToFit() + + } + .padding(.horizontal, 5) + .aspectRatio(1.33, contentMode: .fit) + .cornerRadius(15) + } +} + + +struct ConsoleView_Previews: PreviewProvider { + static var previews: some View { + + let consoles = CDPreview.shared.consoles() + let console_whish = CDPreview.shared.console(name: "Xbox One") + + guard let console = console_whish ?? consoles.first else { + fatalError("No Console found in core data preview") + } + + var view = ConsoleView(console: console) + view.defaultImage = UIImage(named: "PreviewConsole") ?? UIImage() + + return Group { + view + view + } + } +} diff --git a/Zockerhoehle/Views/GameView.swift b/Zockerhoehle/Views/GameCover.swift similarity index 68% rename from Zockerhoehle/Views/GameView.swift rename to Zockerhoehle/Views/GameCover.swift index 08154f7..529d7dd 100644 --- a/Zockerhoehle/Views/GameView.swift +++ b/Zockerhoehle/Views/GameCover.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -struct GameView : View { +struct GameCover : View { @ObservedObject var game : Game let defaultImage = UIImage() @@ -34,3 +34,17 @@ struct GameView : View { } } } + +struct GameCover_Previews: PreviewProvider { + static var previews: some View { + + let game_wish = CDPreview.shared.game(name: "Zelda: Breath of the Wild") + let games = CDPreview.shared.games() + + guard let game = game_wish ?? games.first else { + fatalError("No game found in core data preview") + } + + return GameCover(game: game) + } +} diff --git a/Zockerhoehle/Views/GameDetailView.swift b/Zockerhoehle/Views/GameDetailView.swift index 0725bcc..0086aae 100644 --- a/Zockerhoehle/Views/GameDetailView.swift +++ b/Zockerhoehle/Views/GameDetailView.swift @@ -1,277 +1,151 @@ -// -// GameDetailView.swift -// Zockerhoehle -// -// Created by Julian-Steffen Müller on 08.07.19. -// Copyright © 2019 Julian-Steffen Müller. All rights reserved. -// - +import Foundation import SwiftUI -struct GameSeriesPicker: View { - @ObservedObject var game : Game +struct HeadlinedMultilineText : View { + let headline : String + let multilineText : String - @State private var selectedGameSeries = "" - - @FetchRequest(entity: GameSeries.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)]) - var gameSeries: FetchedResults - var body: some View { - Picker("Spieleserie", selection: $selectedGameSeries) { - Text("Keine").tag("") - ForEach(gameSeries) { game_series in - Text(game_series.name ?? "n/a").tag(game_series.uuid.uuidString) + VStack(spacing: 0) { + HStack { + Text(headline) + .bold() + .font(.headline) + Spacer() } - }.onChange(of: selectedGameSeries) { newValue in - if let game_series = gameSeries.first(where: {$0.uuid.uuidString == newValue}) { - if game.series != game_series { - game.series = game_series - } + Divider() + .padding(.bottom) + + HStack{ + Text(multilineText) + .frame(maxWidth: .infinity, alignment: .leading) } } } - - init(game : Game) { - self.game = game - - if let game_series = game.series { - _selectedGameSeries = State(initialValue: game_series.uuid.uuidString) - } - } } -struct AttachmentCellImage : View { - @ObservedObject var game : Game - var onClick: ()->() - - let defaultImage = UIImage() +struct InfoItem : View { + let headline : String + let content : () -> Content 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) + VStack { + Text("Spielzeit") + .padding(.bottom) + .foregroundColor(.gray) + content() + } + } +} + +struct GameView : View { + @ObservedObject var game : Game + + let columns = [ + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()) + ] + + private func coverPadding(game: Game) -> CGFloat { + if ICloudManager.imageRatio(path: game.cover_icloud_path) < 1 { + return 30 + }else{ + return 0 } } - init(game: Game, onClick: @escaping ()->()) { - self.game = game - self.onClick = onClick + var body: some View { + ScrollView { + VStack { + GameCover(game: game) + .padding(.horizontal, coverPadding(game: game)) + Text("\(game.name)") + .font(.title) + Text("\(game.console?.name ?? "No Console")") + .foregroundColor(.gray) + + + LazyVGrid(columns: columns) { + InfoItem(headline: "Gekauft") { + Text("\(game.pickupOrReleaseDate.formattedInTimeZone(dateFormat: Date.view_date_format))") + .bold() + } + + if game.playtime_h > 0 || game.playtime_min > 0 { + InfoItem(headline: "Spielzeit") { + Text("\(game.playtime_h)h \(game.playtime_min)min") + } + } + + if game.isFinished { + InfoItem(headline: "Durchgezockt") { + Text("Ja") + } + } + + } + .padding(.vertical) + + HeadlinedMultilineText(headline: "Notizen", multilineText: "Ganz toller multiline\nText") + .padding(.bottom) + + HeadlinedMultilineText(headline: "Hinter den Kulissen", multilineText: "Ich habe für das Spiel bei MCMedia Games angestanden") + + Spacer() + } + }.padding(.horizontal, 40) + } +} + +struct GameView_Previews: PreviewProvider { + static var previews: some View { + + let game_wish = CDPreview.shared.game(name: "Zelda - Ocarina of Time") + let games = CDPreview.shared.games() + + guard let game = game_wish ?? games.first else { + fatalError("No game found in core data preview") + } + + return GameView(game: game) + } +} + +struct GameView_Landscape_Cover_Previews: PreviewProvider { + static var previews: some View { + + let game_wish = CDPreview.shared.game(name: "Zelda: Breath of the Wild") + let games = CDPreview.shared.games() + + guard let game = game_wish ?? games.first else { + fatalError("No game found in core data preview") + } + + return GameView(game: game) } } struct GameDetailView : View { @ObservedObject var game : Game - @Environment(\.presentationMode) var presentationMode: Binding - - @State private var showDeleteAlert : Bool = false - - @State var isImportingCover : Bool = false - - let defaultImage = UIImage() - - @State var isLent_raw : Bool = false - private var isLent: Binding { - Binding( - get: { self.isLent_raw || (self.game.lentTo != .none && self.game.lentTo != "") }, - set: { - isLent_raw = $0 - if !$0 { self.game.lentTo = .none } - } - ) - } - - @State var hasFinishedDate_raw : Bool = false - private var hasFinishedDate: Binding { - Binding( - get: { self.hasFinishedDate_raw || self.game.finishedDate != .none }, - set: { - hasFinishedDate_raw = $0 - if !$0 { self.game.finishedDate = .none } - } - ) - } - var finishedDateBinding: Binding { - Binding( - get: { self.game.finishedDate ?? Date() }, - set: { self.game.finishedDate = $0 }) - } - - - var notesBinding: Binding { - Binding( - get: { self.game.notes ?? "" }, - set: { self.game.notes = $0 }) - } - - var pickupDscriptionBinding: Binding { - Binding( - get: { self.game.pickupDescription ?? "" }, - set: { self.game.pickupDescription = $0 }) - } - - var lentToBinding: Binding { - Binding( - get: { self.game.lentTo ?? "" }, - set: { self.game.lentTo = $0 }) - } - - @State var showWrongFolderAlert : Bool = false - 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") - }) - } - - if self.hasFinishedDate.wrappedValue && game.isFinished { - DatePicker("Durchgezockt am", - selection: finishedDateBinding, - in: ...Date(), - displayedComponents: [.date]) - } - } - } + @State private var isInEditMode = false var body: some View { - Form { - Section { - TextField("Videogame name", text: $game.name) - - //Gray color should indicate immutable data - - 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(), - displayedComponents: [.date]) - - HStack { - Text("Anlass") - TextEditor(text: pickupDscriptionBinding).frame(height: 100) - } - - GameSeriesPicker(game: game) - - Toggle(isOn: isLent, label: { - Text("Verliehen?") - }) - - if isLent.wrappedValue { - TextField("Verliehen an", text: lentToBinding) - } - - GameIsFinished - - } - - 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 - } - } - } - - Section{ - gameDeleteButton - } - - } - .navigationBarTitle(Text("\(game.name)"), displayMode: .automatic) - .fileImporter( - isPresented: $isImportingCover, - allowedContentTypes: [.jpeg, .png], - allowsMultipleSelection: false - ) { result in - do { - let selectedFile : URL = try result.get().first! - - //It seems that isUbiquitousItem checks if the file is contained in the Apps iCloud folder - if (ICloudManager.inICloudContainer(url: selectedFile)) { - game.cover_icloud_path = ICloudManager.relativePathFrom(url: selectedFile) - - print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")") - }else{ - self.showWrongFolderAlert = true - } - }catch{ - print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'") + Group { + if !isInEditMode { + GameView(game: game) + }else { + GameEditMode(game: game) } } - .alert(isPresented: $showWrongFolderAlert) { - Alert( - title: Text("Falscher Ordner"), - message: Text("Bitte nutze nur Dateien aus dem Zockerhöhle iCloud Ordner"), - dismissButton: .default(Text("Alles klar!")) - ) + .navigationTitle("\(game.name)") + .toolbar { + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button(action: { + isInEditMode.toggle() + }, + label: { Image(systemName: "plus")}) + } } } - - //Irgendwie gibt es einen Bug der den Alert im navigationBarItem zwei mal aufruft. - //Deswegen muss der delete Button is auf weiteres in die Form - var gameDeleteButton : some View { - Button(action: { - self.showDeleteAlert = true - }, label: { - Spacer() - Text("Spiel löschen") - Spacer() - //Image(systemName: "trash").foregroundColor(.red) - }) - .accentColor(.red) - .alert(isPresented: $showDeleteAlert, content: { - 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"))) - }) - } } diff --git a/Zockerhoehle/Views/GameEditMode.swift b/Zockerhoehle/Views/GameEditMode.swift new file mode 100644 index 0000000..bc9f8f4 --- /dev/null +++ b/Zockerhoehle/Views/GameEditMode.swift @@ -0,0 +1,277 @@ +// +// GameDetailView.swift +// Zockerhoehle +// +// Created by Julian-Steffen Müller on 08.07.19. +// Copyright © 2019 Julian-Steffen Müller. All rights reserved. +// + +import SwiftUI + +struct GameSeriesPicker: View { + @ObservedObject var game : Game + + @State private var selectedGameSeries = "" + + @FetchRequest(entity: GameSeries.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)]) + var gameSeries: FetchedResults + + var body: some View { + Picker("Spieleserie", selection: $selectedGameSeries) { + Text("Keine").tag("") + ForEach(gameSeries) { game_series in + Text(game_series.name ?? "n/a").tag(game_series.uuid.uuidString) + } + }.onChange(of: selectedGameSeries) { newValue in + if let game_series = gameSeries.first(where: {$0.uuid.uuidString == newValue}) { + if game.series != game_series { + game.series = game_series + } + } + } + } + + init(game : Game) { + self.game = game + + if let game_series = game.series { + _selectedGameSeries = State(initialValue: game_series.uuid.uuidString) + } + } +} + +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 GameEditMode : View { + @ObservedObject var game : Game + @Environment(\.presentationMode) var presentationMode: Binding + + @State private var showDeleteAlert : Bool = false + + @State var isImportingCover : Bool = false + + let defaultImage = UIImage() + + @State var isLent_raw : Bool = false + private var isLent: Binding { + Binding( + get: { self.isLent_raw || (self.game.lentTo != .none && self.game.lentTo != "") }, + set: { + isLent_raw = $0 + if !$0 { self.game.lentTo = .none } + } + ) + } + + @State var hasFinishedDate_raw : Bool = false + private var hasFinishedDate: Binding { + Binding( + get: { self.hasFinishedDate_raw || self.game.finishedDate != .none }, + set: { + hasFinishedDate_raw = $0 + if !$0 { self.game.finishedDate = .none } + } + ) + } + var finishedDateBinding: Binding { + Binding( + get: { self.game.finishedDate ?? Date() }, + set: { self.game.finishedDate = $0 }) + } + + + var notesBinding: Binding { + Binding( + get: { self.game.notes ?? "" }, + set: { self.game.notes = $0 }) + } + + var pickupDscriptionBinding: Binding { + Binding( + get: { self.game.pickupDescription ?? "" }, + set: { self.game.pickupDescription = $0 }) + } + + var lentToBinding: Binding { + Binding( + get: { self.game.lentTo ?? "" }, + set: { self.game.lentTo = $0 }) + } + + @State var showWrongFolderAlert : Bool = false + 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") + }) + } + + if self.hasFinishedDate.wrappedValue && game.isFinished { + DatePicker("Durchgezockt am", + selection: finishedDateBinding, + in: ...Date(), + displayedComponents: [.date]) + } + } + } + + var body: some View { + Form { + Section { + TextField("Videogame name", text: $game.name) + + //Gray color should indicate immutable data + + 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.pickupOrReleaseDate, + in: ...Date(), + displayedComponents: [.date]) + + HStack { + Text("Anlass") + TextEditor(text: pickupDscriptionBinding).frame(height: 100) + } + + GameSeriesPicker(game: game) + + Toggle(isOn: isLent, label: { + Text("Verliehen?") + }) + + if isLent.wrappedValue { + TextField("Verliehen an", text: lentToBinding) + } + + GameIsFinished + + } + + 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 + } + } + } + + Section{ + gameDeleteButton + } + + } + .navigationBarTitle(Text("\(game.name)"), displayMode: .automatic) + .fileImporter( + isPresented: $isImportingCover, + allowedContentTypes: [.jpeg, .png], + allowsMultipleSelection: false + ) { result in + do { + let selectedFile : URL = try result.get().first! + + //It seems that isUbiquitousItem checks if the file is contained in the Apps iCloud folder + if (ICloudManager.inICloudContainer(url: selectedFile)) { + game.cover_icloud_path = ICloudManager.relativePathFrom(url: selectedFile) + + print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")") + }else{ + self.showWrongFolderAlert = true + } + }catch{ + print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'") + } + } + .alert(isPresented: $showWrongFolderAlert) { + Alert( + title: Text("Falscher Ordner"), + message: Text("Bitte nutze nur Dateien aus dem Zockerhöhle iCloud Ordner"), + dismissButton: .default(Text("Alles klar!")) + ) + } + } + + //Irgendwie gibt es einen Bug der den Alert im navigationBarItem zwei mal aufruft. + //Deswegen muss der delete Button is auf weiteres in die Form + var gameDeleteButton : some View { + Button(action: { + self.showDeleteAlert = true + }, label: { + Spacer() + Text("Spiel löschen") + Spacer() + //Image(systemName: "trash").foregroundColor(.red) + }) + .accentColor(.red) + .alert(isPresented: $showDeleteAlert, content: { + 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"))) + }) + } +} diff --git a/Zockerhoehle/Views/GamePickupsView.swift b/Zockerhoehle/Views/GamePickupsView.swift index 354ecf7..8509205 100644 --- a/Zockerhoehle/Views/GamePickupsView.swift +++ b/Zockerhoehle/Views/GamePickupsView.swift @@ -16,7 +16,7 @@ struct GamePickupsView: View { init() { let gamesFR = NSFetchRequest(entityName: "Game") - gamesFR.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false), NSSortDescriptor(key: "name", ascending: true)] + gamesFR.sortDescriptors = [NSSortDescriptor(key: "pickupOrReleaseDate", ascending: false), NSSortDescriptor(key: "name", ascending: true)] gamesFR.fetchLimit = 50; _games = FetchRequest(fetchRequest: gamesFR) } @@ -29,8 +29,8 @@ struct GamePickupsView: View { ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(games) { game in - NavigationLink(destination: GameDetailView(game: game)) { - GameView(game: game) + NavigationLink(destination: GameEditMode(game: game)) { + GameCover(game: game) } } }.padding() diff --git a/Zockerhoehle/Views/GameSeriesLibraryView.swift b/Zockerhoehle/Views/GameSeriesLibraryView.swift index 6345ca5..a1a2a9c 100644 --- a/Zockerhoehle/Views/GameSeriesLibraryView.swift +++ b/Zockerhoehle/Views/GameSeriesLibraryView.swift @@ -35,8 +35,8 @@ struct GameSeriesLibraryView: View { ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(games) { game in - NavigationLink(destination: GameDetailView(game: game)) { - GameView(game: game) + NavigationLink(destination: GameEditMode(game: game)) { + GameCover(game: game) } } }.padding() diff --git a/Zockerhoehle/Views/Overview.swift b/Zockerhoehle/Views/Overview.swift index 4921516..2a3293f 100644 --- a/Zockerhoehle/Views/Overview.swift +++ b/Zockerhoehle/Views/Overview.swift @@ -54,7 +54,7 @@ struct EmptyCategory : View { struct Overview: View { @Environment(\.managedObjectContext) private var viewContext - @FetchRequest(entity: Game.entity(), sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false), NSSortDescriptor(key: "name", ascending: true)]) + @FetchRequest(entity: Game.entity(), sortDescriptors: [NSSortDescriptor(key: "pickupOrReleaseDate", ascending: false), NSSortDescriptor(key: "name", ascending: true)]) var games: FetchedResults @FetchRequest(entity: Game.entity(), sortDescriptors: [NSSortDescriptor(key: "finishedDate", ascending: false), NSSortDescriptor(key: "name", ascending: true)]) @@ -115,10 +115,10 @@ struct Overview: View { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { ForEach(games.prefix(last_picksups_limit)) { game in - NavigationLink(destination: GameDetailView(game: game)) { + NavigationLink(destination: GameEditMode(game: game)) { VStack(alignment: .leading) { Group { - GameView(game: game) + GameCover(game: game) } .frame(width: 100, height: 100) @@ -145,10 +145,10 @@ struct Overview: View { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { ForEach(gamesFinished.prefix(last_picksups_limit).filter({$0.isFinished})) { game in - NavigationLink(destination: GameDetailView(game: game)) { + NavigationLink(destination: GameEditMode(game: game)) { VStack(alignment: .leading) { Group { - GameView(game: game) + GameCover(game: game) } .frame(width: 100, height: 100) diff --git a/Zockerhoehle/Zockerhoehle.entitlements b/Zockerhoehle/Zockerhoehle.entitlements index 40c4a24..b37db05 100644 --- a/Zockerhoehle/Zockerhoehle.entitlements +++ b/Zockerhoehle/Zockerhoehle.entitlements @@ -17,5 +17,9 @@ iCloud.Zockerhoehle + com.apple.security.app-sandbox + + com.apple.security.network.client + diff --git a/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents b/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents index 2ecca2b..d57020c 100644 --- a/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents +++ b/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents @@ -23,15 +23,16 @@ - + + @@ -54,7 +55,7 @@ - +