diff --git a/AutoCat/Controllers/CheckController.swift b/AutoCat/Controllers/CheckController.swift index a302bde..79bb621 100644 --- a/AutoCat/Controllers/CheckController.swift +++ b/AutoCat/Controllers/CheckController.swift @@ -11,11 +11,29 @@ enum EventAction: Equatable { case receiveAndSend } -enum HistoryFilter { +enum HistoryFilter: CaseIterable, Identifiable { case all case unrecognized case outdated case notSynced + + var title: String { + switch self { + case .all: return NSLocalizedString("All", comment: ""); + case .unrecognized: return NSLocalizedString("Unrecognized", comment: ""); + case .outdated: return NSLocalizedString("Outdated", comment: ""); + case .notSynced: return NSLocalizedString("Not updated", comment: ""); + } + } + + var id: Int { + switch self { + case .all: return 0; + case .unrecognized: return 1; + case .outdated: return 2; + case .notSynced: return 3; + } + } } extension String.StringInterpolation { diff --git a/AutoCat/Screens/HistoryScreen/HistoryScreen.swift b/AutoCat/Screens/HistoryScreen/HistoryScreen.swift index d33464e..c2923aa 100644 --- a/AutoCat/Screens/HistoryScreen/HistoryScreen.swift +++ b/AutoCat/Screens/HistoryScreen/HistoryScreen.swift @@ -12,6 +12,7 @@ import AutoCatCore struct HistoryScreen: View { @State var viewModel: HistoryViewModel + @State var filterSheetPresented = false var body: some View { List { @@ -24,8 +25,27 @@ struct HistoryScreen: View { } } .listStyle(.plain) - .navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""), viewModel.vehicleSections.reduce(0, { $0 + $1.elements.count }))) + .navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""), + viewModel.vehicleSections.reduce(0, { $0 + $1.elements.count }))) .searchable(text: $viewModel.searchText, prompt: "Search plate numbers") + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .keyboardType(.asciiCapable) + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button("", systemImage: "line.horizontal.3.decrease") { + filterSheetPresented = true + } + } + } + .confirmationDialog("Filter check history", isPresented: $filterSheetPresented, titleVisibility: .visible) { + ForEach(HistoryFilter.allCases) { filter in + Button(filter.title) { + viewModel.filter = filter + viewModel.applyFilters() + } + } + } } } diff --git a/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift index f97bee3..aa4d9ff 100644 --- a/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift +++ b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift @@ -16,17 +16,20 @@ final class HistoryViewModel { let apiService: ApiServiceProtocol let storageService: StorageServiceProtocol + var vehicles: [VehicleDto] = [] + var vehiclesFiltered: [VehicleDto] = [] var vehicleSections: [DateSection] = [] - var vehicleSectionsFiltered: [DateSection] = [] var searchText: String = "" { didSet { if searchText != oldValue { - applySearchFilter(text: searchText) + applyFilters() } } } + var filter: HistoryFilter = .all + init(apiService: ApiServiceProtocol, storageService: StorageServiceProtocol) { self.apiService = apiService @@ -37,11 +40,47 @@ final class HistoryViewModel { func loadVehicles() async { - let vehicles = await storageService.loadVehicles() - vehicleSections = vehicles.groupedByDate(type: .updatedDate) + vehicles = await storageService.loadVehicles() + vehiclesFiltered = vehicles + vehicleSections = vehiclesFiltered.groupedByDate(type: .updatedDate) } - func applySearchFilter(text: String) { + func applyFilters() { + vehiclesFiltered = filterType(vehicles) + vehiclesFiltered = filterSearch(vehiclesFiltered) + vehicleSections = vehiclesFiltered.groupedByDate(type: .updatedDate) + } + + func filterSearch(_ vehicles: [VehicleDto]) -> [VehicleDto] { + guard !searchText.isEmpty else { + return vehicles + } + let text = searchText.uppercased() + let regex = try? Regex(text) + + return vehicles.filter { vehicle in + let number = vehicle.getNumber() + if let regex { + return number.contains(regex) + } else { + return number.contains(text) + } + } + } + + func filterType(_ vehicles: [VehicleDto]) -> [VehicleDto] { + guard filter != .all else { + return vehicles + } + + return vehicles.filter { vehicle in + switch filter { + case .unrecognized: vehicle.unrecognized + case .outdated: vehicle.outdated + case .notSynced: vehicle.needSync + case .all: true + } + } } }