From c4ddbe78604bd20d891051dd6be2e995855bf75b Mon Sep 17 00:00:00 2001 From: Mikaela Caron Date: Sat, 25 Nov 2023 16:08:06 -0500 Subject: [PATCH 1/4] Create EditOdometerReadingView --- .../project.pbxproj | 4 + .../ViewModels/OdometerViewModel.swift | 9 ++ .../Views/EditOdometerReadingView.swift | 101 ++++++++++++++++++ .../Shared/Odometer/Views/OdometerView.swift | 20 ++++ 4 files changed, 134 insertions(+) create mode 100644 Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift diff --git a/Basic-Car-Maintenance.xcodeproj/project.pbxproj b/Basic-Car-Maintenance.xcodeproj/project.pbxproj index f94fa164..4b36d931 100644 --- a/Basic-Car-Maintenance.xcodeproj/project.pbxproj +++ b/Basic-Car-Maintenance.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ FF4E82BE2AD39863004949AF /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = FF4E82BD2AD39863004949AF /* FirebaseRemoteConfig */; }; FF4E82C02AD39863004949AF /* FirebaseRemoteConfigSwift in Frameworks */ = {isa = PBXBuildFile; productRef = FF4E82BF2AD39863004949AF /* FirebaseRemoteConfigSwift */; }; FF4E82C22AD39863004949AF /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = FF4E82C12AD39863004949AF /* FirebaseStorage */; }; + FF50DDFB2B12944900E87362 /* EditOdometerReadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF50DDFA2B12944900E87362 /* EditOdometerReadingView.swift */; }; FF5D13A72A86C2D600BC9BD6 /* BasicCarMaintenanceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D13A62A86C2D600BC9BD6 /* BasicCarMaintenanceApp.swift */; }; FF5D13AB2A86C2D800BC9BD6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF5D13AA2A86C2D800BC9BD6 /* Assets.xcassets */; }; FF5D13AF2A86C2D800BC9BD6 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF5D13AE2A86C2D800BC9BD6 /* Preview Assets.xcassets */; }; @@ -132,6 +133,7 @@ FF09FC902AB6FF44006BE61A /* AuthenticationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationView.swift; sourceTree = ""; }; FF218EF52B00865F0025A533 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; FF3DDF512AA4D28F009D91C4 /* DashboardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardViewModel.swift; sourceTree = ""; }; + FF50DDFA2B12944900E87362 /* EditOdometerReadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditOdometerReadingView.swift; sourceTree = ""; }; FF5D13A32A86C2D600BC9BD6 /* Basic-Car-Maintenance.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Basic-Car-Maintenance.app"; sourceTree = BUILT_PRODUCTS_DIR; }; FF5D13A62A86C2D600BC9BD6 /* BasicCarMaintenanceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicCarMaintenanceApp.swift; sourceTree = ""; }; FF5D13AA2A86C2D800BC9BD6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -223,6 +225,7 @@ children = ( FFE0AF552AD66C3500AB46F8 /* OdometerView.swift */, 57CDD99F2ADC31A8002EFED0 /* AddOdometerReadingView.swift */, + FF50DDFA2B12944900E87362 /* EditOdometerReadingView.swift */, ); path = Views; sourceTree = ""; @@ -723,6 +726,7 @@ E58499682ACDDA9A00634660 /* ContributorsProfileView.swift in Sources */, FF755B492A909A0000F49A13 /* AddMaintenanceView.swift in Sources */, FF09FC912AB6FF44006BE61A /* AuthenticationView.swift in Sources */, + FF50DDFB2B12944900E87362 /* EditOdometerReadingView.swift in Sources */, 023057F22ACFAD79006C5A73 /* EditEventDetailView.swift in Sources */, 637505482AEFFBAC00AA5D0B /* FirebaseAnalytics+Extension.swift in Sources */, 8A3D74862AD6D9A10000FEEB /* AlertView.swift in Sources */, diff --git a/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift b/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift index 1cc732b1..66b591c3 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift @@ -18,6 +18,10 @@ class OdometerViewModel { var showAddErrorAlert = false var isShowingAddOdometerReading = false var errorMessage: String = "" + + var selectedReading: OdometerReading? + var isShowingEditReadingView = false + var vehicles = [Vehicle]() init(userUID: String?) { @@ -77,6 +81,11 @@ class OdometerViewModel { } } + func updateOdometerReading(_ reading: OdometerReading) { + + isShowingEditReadingView = false + } + func getVehicles() async { if let uid = userUID { let db = Firestore.firestore() diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift new file mode 100644 index 00000000..e1083acd --- /dev/null +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift @@ -0,0 +1,101 @@ +// +// EditOdometerReadingView.swift +// Basic-Car-Maintenance +// +// Created by Mikaela Caron on 11/25/23. +// + +import SwiftUI + +struct EditOdometerReadingView: View { + let selectedReading: OdometerReading + + let vehicles: [Vehicle] + let updateTapped: (OdometerReading) -> Void + + @State private var date = Date() + @State private var selectedVehicleID: String? + @State private var isMetric = false + @State private var distance = 0 + + var body: some View { + NavigationStack { + Form { + Section { + HStack { + TextField("Distance", value: $distance, format: .number) + + Picker(selection: $isMetric) { + Text("Miles").tag(false) + Text("Kilometers").tag(true) + } label: { + Text("Preferred units", + comment: "Label for units selected when adding an odometer reading") + } + .pickerStyle(.segmented) + } + } + + Section { + Picker(selection: $selectedVehicleID) { + ForEach(vehicles) { vehicle in + Text(vehicle.name) + .tag(vehicle.id) + } + } label: { + Text("Select a vehicle", + comment: "Picker for selecting a vehicle") + } + .pickerStyle(.menu) + } header: { + Text("VehicleSectionHeader", + comment: "Label for Picker for selecting a vehicle") + } + + DatePicker(selection: $date, displayedComponents: .date) { + Text("Date", comment: "Date picker label") + } + .dynamicTypeSize(...DynamicTypeSize.accessibility2) + } + .onAppear { + setEditReadingValues(selectedReading) + } + .navigationTitle(Text("Add Reading", + comment: "Title for form when adding an odometer reading")) + .toolbar { + ToolbarItem { + Button { + if let selectedVehicleID { + let reading = OdometerReading(date: date, + distance: distance, + isMetric: isMetric, + vehicleID: selectedVehicleID) + updateTapped(reading) + } + } label: { + Text("Update", + comment: "Label for submit button on form to add an entry") + } + .disabled(distance < 0) + } + } + } + .analyticsView("\(Self.self)") + } + + func setEditReadingValues(_ reading: OdometerReading) { + self.date = reading.date + self.selectedVehicleID = reading.vehicleID + self.isMetric = reading.isMetric + self.distance = reading.distance + } +} + +#Preview { + EditOdometerReadingView( + selectedReading: OdometerReading(date: Date(), + distance: 0, + isMetric: false, + vehicleID: ""), + vehicles: []) { _ in } +} diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift index 1b9390f9..f9ec84b2 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift @@ -39,6 +39,17 @@ struct OdometerView: View { } label: { Image(systemName: SFSymbol.trash) } + + Button { + viewModel.selectedReading = reading + viewModel.isShowingEditReadingView = true + } label: { + Label { + Text("Edit") + } icon: { + Image(systemName: SFSymbol.pencil) + } + } } } .listStyle(.inset) @@ -68,6 +79,15 @@ struct OdometerView: View { await viewModel.getOdometerReadings() await viewModel.getVehicles() } + .sheet(isPresented: $viewModel.isShowingEditReadingView) { + if let selectedReading = viewModel.selectedReading { + EditOdometerReadingView(selectedReading: selectedReading, + vehicles: viewModel.vehicles) { updatedReading in + viewModel.updateOdometerReading(updatedReading) + } + } + } + } .analyticsView("\(Self.self)") } From 3a99a3f752768b1950450c6e42fa37af3e8f1e33 Mon Sep 17 00:00:00 2001 From: Mikaela Caron Date: Sat, 25 Nov 2023 16:16:31 -0500 Subject: [PATCH 2/4] Update EditOdometerReadingView --- .../Shared/Localizable.xcstrings | 4 +++ .../Views/EditOdometerReadingView.swift | 30 +++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Basic-Car-Maintenance/Shared/Localizable.xcstrings b/Basic-Car-Maintenance/Shared/Localizable.xcstrings index 39329942..2e1df359 100644 --- a/Basic-Car-Maintenance/Shared/Localizable.xcstrings +++ b/Basic-Car-Maintenance/Shared/Localizable.xcstrings @@ -1588,6 +1588,9 @@ } } }, + "Edit Reading" : { + "comment" : "Title for form when editing an odometer reading" + }, "Failed To Add Vehicle" : { "localizations" : { "be" : { @@ -4280,6 +4283,7 @@ } }, "Update" : { + "comment" : "Label for submit button on form to add an entry", "localizations" : { "be" : { "stringUnit" : { diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift index e1083acd..355eb831 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift @@ -18,6 +18,8 @@ struct EditOdometerReadingView: View { @State private var isMetric = false @State private var distance = 0 + @Environment(\.dismiss) private var dismiss + var body: some View { NavigationStack { Form { @@ -37,19 +39,13 @@ struct EditOdometerReadingView: View { } Section { - Picker(selection: $selectedVehicleID) { - ForEach(vehicles) { vehicle in - Text(vehicle.name) - .tag(vehicle.id) - } - } label: { - Text("Select a vehicle", - comment: "Picker for selecting a vehicle") + if let vehicleName = vehicles + .filter({ $0.id == selectedReading.vehicleID }).first?.name { + Text(vehicleName) + .opacity(0.3) } - .pickerStyle(.menu) } header: { - Text("VehicleSectionHeader", - comment: "Label for Picker for selecting a vehicle") + Text("Vehicle") } DatePicker(selection: $date, displayedComponents: .date) { @@ -60,9 +56,17 @@ struct EditOdometerReadingView: View { .onAppear { setEditReadingValues(selectedReading) } - .navigationTitle(Text("Add Reading", - comment: "Title for form when adding an odometer reading")) + .navigationTitle(Text("Edit Reading", + comment: "Title for form when editing an odometer reading")) .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button { + dismiss() + } label: { + Text("Cancel") + } + } + ToolbarItem { Button { if let selectedVehicleID { From 9b0385c80d56ff01bc8c7c94e506438251449d43 Mon Sep 17 00:00:00 2001 From: Mikaela Caron Date: Sat, 25 Nov 2023 16:36:40 -0500 Subject: [PATCH 3/4] Update odometer reading in Firebase --- .../ViewModels/OdometerViewModel.swift | 20 ++++++++++++++++++- .../Views/EditOdometerReadingView.swift | 15 ++++++-------- .../Shared/Odometer/Views/OdometerView.swift | 8 ++++++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift b/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift index 66b591c3..cd227486 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/ViewModels/OdometerViewModel.swift @@ -19,6 +19,7 @@ class OdometerViewModel { var isShowingAddOdometerReading = false var errorMessage: String = "" + var showEditErrorAlert = false var selectedReading: OdometerReading? var isShowingEditReadingView = false @@ -83,7 +84,24 @@ class OdometerViewModel { func updateOdometerReading(_ reading: OdometerReading) { - isShowingEditReadingView = false + if let userUID = userUID { + guard let id = reading.id else { return } + + var readingToUpdate = reading + readingToUpdate.userID = userUID + + do { + try Firestore.firestore() + .collection(FirestorePath.odometerReadings(vehicleID: readingToUpdate.vehicleID).path) + .document(id) + .setData(from: readingToUpdate) + + isShowingEditReadingView = false + } catch { + errorMessage = error.localizedDescription + showEditErrorAlert = true + } + } } func getVehicles() async { diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift index 355eb831..f0fe81f8 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift @@ -14,7 +14,6 @@ struct EditOdometerReadingView: View { let updateTapped: (OdometerReading) -> Void @State private var date = Date() - @State private var selectedVehicleID: String? @State private var isMetric = false @State private var distance = 0 @@ -69,13 +68,12 @@ struct EditOdometerReadingView: View { ToolbarItem { Button { - if let selectedVehicleID { - let reading = OdometerReading(date: date, - distance: distance, - isMetric: isMetric, - vehicleID: selectedVehicleID) - updateTapped(reading) - } + let reading = OdometerReading(id: selectedReading.id, + date: date, + distance: distance, + isMetric: isMetric, + vehicleID: selectedReading.vehicleID) + updateTapped(reading) } label: { Text("Update", comment: "Label for submit button on form to add an entry") @@ -89,7 +87,6 @@ struct EditOdometerReadingView: View { func setEditReadingValues(_ reading: OdometerReading) { self.date = reading.date - self.selectedVehicleID = reading.vehicleID self.isMetric = reading.isMetric self.distance = reading.distance } diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift index f9ec84b2..491ca8d2 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift @@ -81,10 +81,14 @@ struct OdometerView: View { } .sheet(isPresented: $viewModel.isShowingEditReadingView) { if let selectedReading = viewModel.selectedReading { - EditOdometerReadingView(selectedReading: selectedReading, - vehicles: viewModel.vehicles) { updatedReading in + EditOdometerReadingView(selectedReading: selectedReading, vehicles: viewModel.vehicles) { updatedReading in viewModel.updateOdometerReading(updatedReading) } + .alert("An Error Occurred", isPresented: $viewModel.showEditErrorAlert) { + Button("OK", role: .cancel) { } + } message: { + Text(viewModel.errorMessage) + } } } From 4dca74320c1e6e9713b033d7aad77cd2d6eb2b22 Mon Sep 17 00:00:00 2001 From: Mikaela Caron Date: Sat, 25 Nov 2023 16:40:12 -0500 Subject: [PATCH 4/4] Update comment --- Basic-Car-Maintenance/Shared/Localizable.xcstrings | 2 +- .../Shared/Odometer/Views/EditOdometerReadingView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Basic-Car-Maintenance/Shared/Localizable.xcstrings b/Basic-Car-Maintenance/Shared/Localizable.xcstrings index 2e1df359..e3b6abb3 100644 --- a/Basic-Car-Maintenance/Shared/Localizable.xcstrings +++ b/Basic-Car-Maintenance/Shared/Localizable.xcstrings @@ -4283,7 +4283,7 @@ } }, "Update" : { - "comment" : "Label for submit button on form to add an entry", + "comment" : "Label for submit button on form to update an existing entry", "localizations" : { "be" : { "stringUnit" : { diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift index f0fe81f8..28ab794d 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/EditOdometerReadingView.swift @@ -76,7 +76,7 @@ struct EditOdometerReadingView: View { updateTapped(reading) } label: { Text("Update", - comment: "Label for submit button on form to add an entry") + comment: "Label for submit button on form to update an existing entry") } .disabled(distance < 0) }