Fixing issue with filtering by dates
This commit is contained in:
parent
796ad90b6c
commit
46a70ef611
@ -106,6 +106,7 @@
|
||||
7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE712469DFB600ABE48E /* MediaBrowserViewController.swift */; };
|
||||
7A64AE762469DFB600ABE48E /* ContentTransformers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE722469DFB600ABE48E /* ContentTransformers.swift */; };
|
||||
7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A659B5A24A3768A0043A0F2 /* Substrings.swift */; };
|
||||
7A6B65B32CFB0DB500AABA6B /* NullifyDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B65B22CFB0DB500AABA6B /* NullifyDate.swift */; };
|
||||
7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */ = {isa = PBXBuildFile; productRef = 7A6C4D9D2C56BCA600982597 /* SwiftLocation */; };
|
||||
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD902242BF4A5009DE740 /* PlateView.swift */; };
|
||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90724329144009DE740 /* CenterTextLayer.swift */; };
|
||||
@ -148,6 +149,7 @@
|
||||
7AAAFADC2C4D1E130050410D /* ACImageSliderView+Modifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADB2C4D1E130050410D /* ACImageSliderView+Modifier.swift */; };
|
||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */; };
|
||||
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; };
|
||||
7AABBE3B2CF9F85600346588 /* Binding+Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABBE3A2CF9F85600346588 /* Binding+Map.swift */; };
|
||||
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */; };
|
||||
7AB0EF812C5CC0FE00291EE6 /* SwiftLocationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */; };
|
||||
7AB0EF892C5D307600291EE6 /* LocationServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF882C5D307600291EE6 /* LocationServiceStub.swift */; };
|
||||
@ -380,6 +382,7 @@
|
||||
7A64AE722469DFB600ABE48E /* ContentTransformers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentTransformers.swift; sourceTree = "<group>"; };
|
||||
7A659B5824A2B1BA0043A0F2 /* AudioRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecord.swift; sourceTree = "<group>"; };
|
||||
7A659B5A24A3768A0043A0F2 /* Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Substrings.swift; sourceTree = "<group>"; };
|
||||
7A6B65B22CFB0DB500AABA6B /* NullifyDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NullifyDate.swift; sourceTree = "<group>"; };
|
||||
7A6DD902242BF4A5009DE740 /* PlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = "<group>"; };
|
||||
7A6DD90724329144009DE740 /* CenterTextLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTextLayer.swift; sourceTree = "<group>"; };
|
||||
7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = "<group>"; };
|
||||
@ -418,6 +421,7 @@
|
||||
7AAAFAD92C4D1AFE0050410D /* Zoomable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zoomable.swift; sourceTree = "<group>"; };
|
||||
7AAAFADB2C4D1E130050410D /* ACImageSliderView+Modifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ACImageSliderView+Modifier.swift"; sourceTree = "<group>"; };
|
||||
7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderModel.swift; sourceTree = "<group>"; };
|
||||
7AABBE3A2CF9F85600346588 /* Binding+Map.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Map.swift"; sourceTree = "<group>"; };
|
||||
7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxSectionedDataSource.swift; sourceTree = "<group>"; };
|
||||
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
||||
7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLocationProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -1058,6 +1062,7 @@
|
||||
7A27ADF824A09CAD0035F39E /* CocoaError.swift */,
|
||||
7AE8424D26109F78002F6B31 /* Exportable.swift */,
|
||||
7A1CF81529A42117007962DA /* Realm.swift */,
|
||||
7A6B65B22CFB0DB500AABA6B /* NullifyDate.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -1075,6 +1080,7 @@
|
||||
7AF8606F2CBAA24500954D2F /* NavigationLink.swift */,
|
||||
7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */,
|
||||
7A4927D42CCE438600851C01 /* OptionalBinding.swift */,
|
||||
7AABBE3A2CF9F85600346588 /* Binding+Map.swift */,
|
||||
);
|
||||
path = SwiftUI;
|
||||
sourceTree = "<group>";
|
||||
@ -1354,6 +1360,7 @@
|
||||
7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */,
|
||||
7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */,
|
||||
7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */,
|
||||
7AABBE3B2CF9F85600346588 /* Binding+Map.swift in Sources */,
|
||||
7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */,
|
||||
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */,
|
||||
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */,
|
||||
@ -1452,6 +1459,7 @@
|
||||
7A64A2182C19E64800284124 /* VehicleOwnershipPeriodDto.swift in Sources */,
|
||||
7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */,
|
||||
7A64A2162C19E4CF00284124 /* VehiclePhotoDto.swift in Sources */,
|
||||
7A6B65B32CFB0DB500AABA6B /* NullifyDate.swift in Sources */,
|
||||
7A7097C22C9EC139007CFDCA /* ServiceContainer.swift in Sources */,
|
||||
7A7097C62C9EC77A007CFDCA /* ServicePropertyWrapper.swift in Sources */,
|
||||
7A5D84BE2C1AE44700C2209B /* VehiclePhoto.swift in Sources */,
|
||||
@ -1697,7 +1705,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 141;
|
||||
CURRENT_PROJECT_VERSION = 142;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||
@ -1724,7 +1732,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 141;
|
||||
CURRENT_PROJECT_VERSION = 142;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||
|
||||
@ -61,4 +61,12 @@ class FiltersViewModel {
|
||||
func applyFilters() {
|
||||
filterResult = filter
|
||||
}
|
||||
|
||||
func nullifyTime(of date: Date?) -> Date? {
|
||||
guard let date else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: date)
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ class ReportCoordinator: Coordinator {
|
||||
navController = viewController?.viewControllers.last as? UINavigationController
|
||||
} else {
|
||||
let viewModel = ReportViewModel(vehicle: vehicle)
|
||||
viewModel.coordinator = self
|
||||
let controller = UIHostingController(rootView: ReportScreen(viewModel: viewModel))
|
||||
navController = UINavigationController(rootViewController: controller)
|
||||
}
|
||||
@ -41,4 +42,36 @@ class ReportCoordinator: Coordinator {
|
||||
viewController?.showDetailViewController(navController, sender: self)
|
||||
}
|
||||
}
|
||||
|
||||
func openEvents(vehicle: VehicleDto) {
|
||||
|
||||
let sb = UIStoryboard(name: "Main", bundle: nil)
|
||||
let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController
|
||||
controller.vehicle = self.vehicle
|
||||
controller.vehicleUpdated = { vehicle in
|
||||
// TODO: Propagate vehicle update upwards
|
||||
//self.vehicle = vehicle
|
||||
}
|
||||
navController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
func openOsago(contracts: [OsagoDto]) {
|
||||
guard let navController else { return }
|
||||
|
||||
Task {
|
||||
let coordinator = OsagoCoordinator(navController: navController, contracts: contracts)
|
||||
try? await coordinator.start()
|
||||
}
|
||||
}
|
||||
|
||||
func openOwners(ownerships: [VehicleOwnershipPeriodDto]) {
|
||||
guard let navController else {
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
let coordiantor = OwnersCoordinator(navController: navController, ownerships: ownerships)
|
||||
try? await coordiantor.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,10 +32,32 @@ struct ReportScreen: View {
|
||||
LabeledContent("Year", value: String(viewModel.vehicle.year))
|
||||
LabeledContent("Color", value: viewModel.vehicle.color ?? "")
|
||||
LabeledContent("Category", value: viewModel.vehicle.category ?? "")
|
||||
LabeledContent("Steering wheel position",
|
||||
value: viewModel.vehicle.isRightWheel == true ? "Right" : "Left")
|
||||
LabeledContent("Japanese",
|
||||
value: viewModel.vehicle.isJapanese == true ? "Yes" : "No")
|
||||
LabeledContent("Steering wheel position", value: viewModel.steerignWheelPosition)
|
||||
LabeledContent("Japanese", value: viewModel.isJapanese)
|
||||
}
|
||||
|
||||
Section("Identifiers") {
|
||||
LabeledContent("Plate number", value: viewModel.plateNumber)
|
||||
LabeledContent("VIN", value: viewModel.vehicle.vin1 ?? "")
|
||||
LabeledContent("STS", value: viewModel.vehicle.sts ?? "")
|
||||
LabeledContent("PTS", value: viewModel.vehicle.pts ?? "")
|
||||
}
|
||||
|
||||
Section("Engine") {
|
||||
LabeledContent("Number", value: viewModel.vehicle.engine?.number ?? "")
|
||||
LabeledContent("Fuel type", value: viewModel.vehicle.engine?.fuelType ?? "")
|
||||
LabeledContent("Volume (cm³)", value: String(viewModel.vehicle.engine?.volume ?? 0))
|
||||
LabeledContent("Power (HP)", value: String(viewModel.vehicle.engine?.powerHp ?? 0))
|
||||
LabeledContent("Power (kw)", value: String(viewModel.vehicle.engine?.powerKw ?? 0))
|
||||
}
|
||||
|
||||
Section("History") {
|
||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||
.navigationLink(onTap: viewModel.openEvents)
|
||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||
.navigationLink(onTap: viewModel.openOsago)
|
||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||
.navigationLink(onTap: viewModel.openOwners)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,10 +16,44 @@ class ReportViewModel {
|
||||
@ObservationIgnored @Service var api: ApiServiceProtocol
|
||||
@ObservationIgnored @Service var storageService: StorageServiceProtocol
|
||||
|
||||
var coordinator: ReportCoordinator?
|
||||
|
||||
var vehicle: VehicleDto
|
||||
var hud: ACHud?
|
||||
|
||||
var plateNumber: String {
|
||||
if vehicle.outdated, let current = vehicle.currentNumber {
|
||||
"\(vehicle.number) (\(current))"
|
||||
} else {
|
||||
vehicle.number
|
||||
}
|
||||
}
|
||||
|
||||
var steerignWheelPosition: String {
|
||||
vehicle.isRightWheel == true
|
||||
? NSLocalizedString("Right", comment: "")
|
||||
: NSLocalizedString("Left", comment: "")
|
||||
}
|
||||
|
||||
var isJapanese: String {
|
||||
vehicle.isJapanese == true
|
||||
? NSLocalizedString("Yes", comment: "")
|
||||
: NSLocalizedString("No", comment: "")
|
||||
}
|
||||
|
||||
init(vehicle: VehicleDto) {
|
||||
self.vehicle = vehicle
|
||||
}
|
||||
|
||||
func openEvents() {
|
||||
coordinator?.openEvents(vehicle: vehicle)
|
||||
}
|
||||
|
||||
func openOsago() {
|
||||
coordinator?.openOsago(contracts: vehicle.osagoContracts)
|
||||
}
|
||||
|
||||
func openOwners() {
|
||||
coordinator?.openOwners(ownerships: vehicle.ownershipPeriods)
|
||||
}
|
||||
}
|
||||
|
||||
20
AutoCat/SwiftUI/Binding+Map.swift
Normal file
20
AutoCat/SwiftUI/Binding+Map.swift
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Binding+Map.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 29.11.2024.
|
||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Binding where Value: Sendable {
|
||||
|
||||
func map(_ transform: @escaping @Sendable (Value) -> Value) -> Binding<Value> {
|
||||
|
||||
Binding<Value>(
|
||||
get: { transform(wrappedValue) },
|
||||
set: { wrappedValue = transform($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -10,10 +10,11 @@ import SwiftUI
|
||||
|
||||
struct NavigationLinkModifier: ViewModifier {
|
||||
|
||||
var onTap: () -> Void
|
||||
var onTap: (() -> Void)?
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
|
||||
if let onTap {
|
||||
HStack(spacing: 0) {
|
||||
content
|
||||
Spacer()
|
||||
@ -24,12 +25,15 @@ struct NavigationLinkModifier: ViewModifier {
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture(perform: onTap)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
func navigationLink(onTap: @escaping () -> Void) -> some View {
|
||||
func navigationLink(onTap: (() -> Void)?) -> some View {
|
||||
modifier(NavigationLinkModifier(onTap: onTap))
|
||||
}
|
||||
}
|
||||
|
||||
31
AutoCatCore/Extensions/NullifyDate.swift
Normal file
31
AutoCatCore/Extensions/NullifyDate.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// NullifyDate.swift
|
||||
// AutoCatCore
|
||||
//
|
||||
// Created by Selim Mustafaev on 30.11.2024.
|
||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@propertyWrapper
|
||||
public struct NullifyDate: Sendable {
|
||||
|
||||
public var wrappedValue: Date? {
|
||||
didSet {
|
||||
wrappedValue = nullifyTime(of: wrappedValue)
|
||||
}
|
||||
}
|
||||
|
||||
public init(wrappedValue: Date?) {
|
||||
self.wrappedValue = nullifyTime(of: wrappedValue)
|
||||
}
|
||||
|
||||
func nullifyTime(of date: Date?) -> Date? {
|
||||
guard let date else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: date)
|
||||
}
|
||||
}
|
||||
@ -98,12 +98,12 @@ public struct Filter: Sendable {
|
||||
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?
|
||||
public var toDateUpdated: Date?
|
||||
public var fromLocationDate: Date?
|
||||
public var toLocationDate: Date?
|
||||
@NullifyDate public var fromDate: Date?
|
||||
@NullifyDate public var toDate: Date?
|
||||
@NullifyDate public var fromDateUpdated: Date?
|
||||
@NullifyDate public var toDateUpdated: Date?
|
||||
@NullifyDate public var fromLocationDate: Date?
|
||||
@NullifyDate public var toLocationDate: Date?
|
||||
public var needReset: Bool = false
|
||||
public var scope: SearchScope = .plateNumber
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
import AutoCatCore
|
||||
import Mockable
|
||||
@ -114,4 +115,28 @@ struct FiltersTests {
|
||||
#expect(viewModel.filter.year == .any)
|
||||
#expect(viewModel.filter.model == .any)
|
||||
}
|
||||
|
||||
@Test("Nullify time in dates")
|
||||
func nullifyTimeInDates() async throws {
|
||||
|
||||
let testTimestamp: TimeInterval = 1732958508
|
||||
let nullifiedTimestamp: TimeInterval = 1732914000
|
||||
|
||||
// Date with non-zero time components
|
||||
let date = Date(timeIntervalSince1970: testTimestamp)
|
||||
|
||||
viewModel.filter.fromDate = date
|
||||
viewModel.filter.toDate = date
|
||||
viewModel.filter.fromDateUpdated = date
|
||||
viewModel.filter.toDateUpdated = date
|
||||
viewModel.filter.fromLocationDate = date
|
||||
viewModel.filter.toLocationDate = date
|
||||
|
||||
#expect(viewModel.filter.fromDate?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
#expect(viewModel.filter.toDate?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
#expect(viewModel.filter.fromDateUpdated?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
#expect(viewModel.filter.toDateUpdated?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
#expect(viewModel.filter.fromLocationDate?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
#expect(viewModel.filter.toLocationDate?.timeIntervalSince1970 == nullifiedTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user