More filters screen code

This commit is contained in:
Selim Mustafaev 2024-10-27 22:20:24 +03:00
parent 8718042f1d
commit ba09884a65
12 changed files with 253 additions and 73 deletions

View File

@ -49,6 +49,7 @@
7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF4249FD2F90035F39E /* FileManagerExt.swift */; };
7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF6249FEF690035F39E /* Recorder.swift */; };
7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */; };
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; };
7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; };
7A33381124990DAE00D878F1 /* FiltersController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A33381024990DAE00D878F1 /* FiltersController.swift */; };
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
@ -61,6 +62,8 @@
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; };
7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */; };
7A45FB382C27073700618694 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45FB372C27073700618694 /* StorageService.swift */; };
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; };
7A4927D72CCEA6DC00851C01 /* VMResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D62CCEA6DC00851C01 /* VMResult.swift */; };
7A530B7A24001D3300CBFE6E /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7924001D3300CBFE6E /* CheckController.swift */; };
7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */; };
7A599C362C18AC7F00D47C18 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C352C18AC7F00D47C18 /* ApiError.swift */; };
@ -306,6 +309,7 @@
7A27ADF824A09CAD0035F39E /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAlertModifier.swift; sourceTree = "<group>"; };
7A2DE69725868AC800A113FC /* VehicleAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleAd.swift; sourceTree = "<group>"; };
7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalDatePicker.swift; sourceTree = "<group>"; };
7A2E6FA32C42B3AD00C40DA7 /* AutoCatCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
7A33381024990DAE00D878F1 /* FiltersController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersController.swift; sourceTree = "<group>"; };
7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; };
@ -319,6 +323,8 @@
7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersCoordinator.swift; sourceTree = "<group>"; };
7A43F9F7246C8A6200BA5B49 /* JWT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = "<group>"; };
7A45FB372C27073700618694 /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = "<group>"; };
7A4927D42CCE438600851C01 /* OptionalBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalBinding.swift; sourceTree = "<group>"; };
7A4927D62CCEA6DC00851C01 /* VMResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMResult.swift; sourceTree = "<group>"; };
7A52AB292580112E002CD910 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
7A530B7924001D3300CBFE6E /* CheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckController.swift; sourceTree = "<group>"; };
7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleCell.swift; sourceTree = "<group>"; };
@ -615,6 +621,7 @@
7AC3554B29696A1C00889457 /* MainTabController.swift */,
7AC3554D29696C4500889457 /* DummyNewController.swift */,
7AC3554F29696D5A00889457 /* NewNumberController.swift */,
7A4927D62CCEA6DC00851C01 /* VMResult.swift */,
);
path = Controllers;
sourceTree = "<group>";
@ -1038,6 +1045,8 @@
7A1022712C554A1300B84627 /* CustomHostingController.swift */,
7A5D7E0B2C71EB25002C17E7 /* ToggleRowView.swift */,
7AF8606F2CBAA24500954D2F /* NavigationLink.swift */,
7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */,
7A4927D42CCE438600851C01 /* OptionalBinding.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
@ -1290,14 +1299,17 @@
7AFBE8C02C3024E5003C491D /* ACHud.swift in Sources */,
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */,
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */,
7A4927D72CCEA6DC00851C01 /* VMResult.swift in Sources */,
7AAAFADA2C4D1AFE0050410D /* Zoomable.swift in Sources */,
7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */,
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */,
7A761C0B267E8FF90005F28F /* Error.swift in Sources */,
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */,
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */,
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */,
7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */,
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */,
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */,

View File

@ -134,7 +134,7 @@ class FiltersController: FormViewController {
row.title = NSLocalizedString("Added by", comment: "")
row.selectorTitle = NSLocalizedString("Added by", comment: "")
row.options = AddedBy.allCases.map { $0.description }
row.value = self.filter.addedBy?.description ?? AddedBy.anyone.description
row.value = self.filter.addedBy.description
}
.onChange { row in
if let index = row.options?.firstIndex(of: row.value ?? "") {
@ -144,7 +144,7 @@ class FiltersController: FormViewController {
}
}
.cellUpdate { cell, row in
row.value = self.filter.addedBy?.description ?? AddedBy.anyone.description
row.value = self.filter.addedBy.description
}
}
@ -210,14 +210,14 @@ class FiltersController: FormViewController {
row.value = self.filter.sortBy
row.options = SortParameter.allCases
}
.onChange { self.filter.sortBy = $0.value }
.onChange { self.filter.sortBy = $0.value ?? .updatedDate }
.cellUpdate { $1.value = self.filter.sortBy }
<<< SegmentedRow<AutoCatCore.SortOrder>("SortOrder") { row in
row.title = NSLocalizedString("Order", comment: "sort order")
row.value = self.filter.sortOrder
row.options = AutoCatCore.SortOrder.allCases
}
.onChange { self.filter.sortOrder = $0.value }
.onChange { self.filter.sortOrder = $0.value ?? .descending }
.cellUpdate { $1.value = self.filter.sortOrder }
}

View File

@ -184,26 +184,28 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe
}
func showFilter() async throws {
let sb = UIStoryboard(name: "Main", bundle: nil)
let controller = sb.instantiateViewController(identifier: "FiltersController") as FiltersController
controller.filter = self.filter
controller.onDone = {
self.filter = controller.filter
self.datasource.setSortParameter(self.filter.sortBy ?? .updatedDate)
self.filter.needReset = true
self.filter.scope = SearchScope(rawValue: self.searchController.searchBar.selectedScopeButtonIndex) ?? .plateNumber
self.updateSearchResults(with: self.filter)
}
self.navigationController?.pushViewController(controller, animated: true)
// if let navigationController = self.navigationController {
// let coordinator = FiltersCoordinator(navController: navigationController, filter: filter)
// filter = try await coordinator.start()
// datasource.setSortParameter(self.filter.sortBy ?? .updatedDate)
// filter.needReset = true
// filter.scope = SearchScope(rawValue: self.searchController.searchBar.selectedScopeButtonIndex) ?? .plateNumber
// updateSearchResults(with: self.filter)
// let sb = UIStoryboard(name: "Main", bundle: nil)
// let controller = sb.instantiateViewController(identifier: "FiltersController") as FiltersController
// controller.filter = self.filter
// controller.onDone = {
// self.filter = controller.filter
// self.datasource.setSortParameter(self.filter.sortBy ?? .updatedDate)
// self.filter.needReset = true
// self.filter.scope = SearchScope(rawValue: self.searchController.searchBar.selectedScopeButtonIndex) ?? .plateNumber
// self.updateSearchResults(with: self.filter)
// }
// self.navigationController?.pushViewController(controller, animated: true)
if let navigationController = self.navigationController {
let coordinator = FiltersCoordinator(navController: navigationController, filter: filter)
if let newFilter = try await coordinator.start() {
filter = newFilter
datasource.setSortParameter(self.filter.sortBy)
filter.needReset = true
filter.scope = SearchScope(rawValue: self.searchController.searchBar.selectedScopeButtonIndex) ?? .plateNumber
updateSearchResults(with: self.filter)
}
}
}
func showOnMap() {

View File

@ -0,0 +1,39 @@
//
// VMResult.swift
// AutoCat
//
// Created by Selim Mustafaev on 27.10.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Foundation
@Observable
class VMResult<T> {
var value: T
var isDone: Bool
init(value: T) {
self.value = value
self.isDone = false
}
func done() {
isDone = true
}
func cancel() {
isDone = false
}
func set(_ value: T) {
self.value = value
isDone = true
}
func get() -> T? {
isDone ? value : nil
}
}

View File

@ -21,11 +21,11 @@ class FiltersCoordinator: Coordinator {
self.filter = filter
}
func start() async throws -> Filter {
func start() async throws -> Filter? {
let viewModel = FiltersViewModel(filter: filter)
let controller = CustomHostingController(rootView: FiltersScreen(viewModel: viewModel))
viewController?.pushViewController(controller, animated: true)
await controller.waitForDisappear()
return viewModel.filter
return viewModel.filterResult
}
}

View File

@ -41,11 +41,75 @@ struct FiltersScreen: View {
}
}
.pickerStyle(.navigationLink)
Section {
Picker("Added by", selection: $viewModel.filter.addedBy) {
ForEach(AddedBy.allCases, id: \.self) {
Text($0.description)
}
}
}
Section("Update time") {
OptionalDatePicker(label: "From",
defaultValue: "Beginning",
date: $viewModel.filter.fromDateUpdated)
OptionalDatePicker(label: "To",
defaultValue: "Now",
date: $viewModel.filter.toDateUpdated)
}
Section("Added time") {
OptionalDatePicker(label: "From",
defaultValue: "Beginning",
date: $viewModel.filter.fromDate)
OptionalDatePicker(label: "To",
defaultValue: "Now",
date: $viewModel.filter.toDate)
}
Section("Location adding time") {
OptionalDatePicker(label: "From",
defaultValue: "Beginning",
date: $viewModel.filter.fromLocationDate)
OptionalDatePicker(label: "To",
defaultValue: "Now",
date: $viewModel.filter.toLocationDate)
}
Section("Sort") {
Section {
Picker("Sort by", selection: $viewModel.filter.sortBy) {
ForEach(SortParameter.allCases, id: \.self) {
Text($0.description)
}
}
Picker("Order", selection: $viewModel.filter.sortOrder) {
ForEach(SortOrder.allCases, id: \.self) {
Text($0.description)
}
}
}
}
Section {
HStack {
Spacer()
Button("Clear all filters") {
viewModel.clearAllFilters()
}
Spacer()
}
}
}
.navigationTitle("Filters")
.navigationBarItems(trailing: Button("Done", action: {
}))
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
viewModel.applyFilters()
}
}
}
.task {
await viewModel.loadData()
}

View File

@ -26,6 +26,8 @@ class FiltersViewModel {
}
}
@ObservationIgnored var filterResult: Filter?
var models: [StringOption] = [.any]
var brands: [StringOption] = [.any]
var colors: [StringOption] = [.any]
@ -51,4 +53,12 @@ class FiltersViewModel {
models = [.any] + ((try? await api.getModels(of: brand)) ?? []).map { .value($0) }
filter.model = .any
}
func clearAllFilters() {
filter.clear()
}
func applyFilters() {
filterResult = filter
}
}

View File

@ -14,7 +14,6 @@ struct LinkRowView: View {
let value: String?
var body: some View {
if #available(iOS 16.0, *) {
ViewThatFits(in: .horizontal) {
SimpleLinkRowView(title: title, value: value)
VStack(alignment: .leading, spacing: 8) {
@ -29,9 +28,6 @@ struct LinkRowView: View {
}
}
}
} else {
SimpleLinkRowView(title: title, value: value)
}
}
}

View File

@ -0,0 +1,16 @@
//
// OptionalBinding.swift
// AutoCat
//
// Created by Selim Mustafaev on 27.10.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
public func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> where T: Sendable {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}

View File

@ -0,0 +1,49 @@
//
// OptionalDatePicker.swift
// AutoCat
//
// Created by Selim Mustafaev on 27.10.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
struct OptionalDatePicker: View {
let label: LocalizedStringKey
let defaultValue: LocalizedStringKey
@Binding var date: Date?
var body: some View {
HStack {
Text(label)
Spacer(minLength: 0)
if date != nil {
HStack {
DatePicker("", selection: $date ?? Date(), displayedComponents: [.date])
.labelsHidden()
Image(systemName: "xmark.circle.fill")
.onTapGesture {
date = nil
}
}
} else {
Text(defaultValue)
.foregroundStyle(.secondary)
}
}
.contentShape(Rectangle())
.onTapGesture {
if date == nil {
date = Date()
}
}
}
}
#Preview {
VStack {
OptionalDatePicker(label: "From", defaultValue: "Beginning", date: .constant(nil))
OptionalDatePicker(label: "To", defaultValue: "Now", date: .constant(Date()))
}
}

View File

@ -21,7 +21,6 @@ struct TextRowView: View {
}
var body: some View {
if #available(iOS 16.0, *) {
ViewThatFits(in: .horizontal) {
SimpleTextRowView(title: title, value: value, showArrow: showArrow)
VStack(alignment: .leading, spacing: 8) {
@ -40,9 +39,6 @@ struct TextRowView: View {
}
}
}
} else {
SimpleTextRowView(title: title, value: value, showArrow: showArrow)
}
}
}

View File

@ -95,9 +95,9 @@ public struct Filter: Sendable {
public var color: StringOption = .any
public var year: StringOption = .any
public var regions: [Int]?
public var addedBy: AddedBy?
public var sortBy: SortParameter? = .updatedDate
public var sortOrder: SortOrder? = .descending
public var addedBy: AddedBy = .anyone
public var sortBy: SortParameter = .updatedDate
public var sortOrder: SortOrder = .descending
public var fromDate: Date?
public var toDate: Date?
public var fromDateUpdated: Date?
@ -116,7 +116,7 @@ public struct Filter: Sendable {
self.color = .any
self.year = .any
self.regions = nil
self.addedBy = nil
self.addedBy = .anyone
self.sortBy = .updatedDate
self.sortOrder = .descending
self.fromDate = nil
@ -129,7 +129,12 @@ public struct Filter: Sendable {
}
public func queryDictionary() -> [String: String] {
var dict: [String: String] = ["query": self.searchString]
var dict: [String: String] = [
"query": self.searchString,
"addedBy": addedBy.rawValue,
"sortBy": sortBy.rawValue,
"sortOrder": sortOrder.rawValue
]
if case let .value(brand) = brand {
dict["brand"] = brand
@ -146,15 +151,6 @@ public struct Filter: Sendable {
if let regions = self.regions {
dict["regions"] = regions.map(String.init).joined(separator: ",")
}
if let addedBy = self.addedBy {
dict["addedBy"] = addedBy.rawValue
}
if let sortBy = self.sortBy {
dict["sortBy"] = sortBy.rawValue
}
if let sortOrder = self.sortOrder {
dict["sortOrder"] = sortOrder.rawValue
}
if let fromDate = self.fromDate {
dict["fromDate"] = String(fromDate.timeIntervalSince1970)
}