Adding split view for ipad/mac
This commit is contained in:
parent
e7a9f25845
commit
544f81be5e
@ -37,7 +37,6 @@
|
||||
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 */; };
|
||||
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
|
||||
7A386A402DABDC190051676A /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A3F2DABDC190051676A /* MapScreen.swift */; };
|
||||
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A432DABDC360051676A /* MapViewModel.swift */; };
|
||||
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A472DABE0D00051676A /* MapMarkerModel.swift */; };
|
||||
@ -162,11 +161,14 @@
|
||||
7ABDA80F2D8723F90083C715 /* StorageService+AudioRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA80E2D8723F90083C715 /* StorageService+AudioRecords.swift */; };
|
||||
7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7AC355492969652F00889457 /* SwiftEntryKit */; };
|
||||
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3554F29696D5A00889457 /* NewNumberController.swift */; };
|
||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555129696E3F00889457 /* UIView+layout.swift */; };
|
||||
7AC35554296973E100889457 /* ACButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC35553296973E100889457 /* ACButton.swift */; };
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC355582969746600889457 /* UIControl.swift */; };
|
||||
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555A296995B200889457 /* UIEdgeInsets.swift */; };
|
||||
7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B812DB390B900ADC026 /* MainTabScreen.swift */; };
|
||||
7AC44B842DB3EF7A00ADC026 /* HistorySplitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */; };
|
||||
7AC44B862DB40A5C00ADC026 /* HideTabBarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */; };
|
||||
7AC44B882DB438F200ADC026 /* UIView+layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B872DB438F200ADC026 /* UIView+layout.swift */; };
|
||||
7AC44B8A2DB4395300ADC026 /* SearchSplitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B892DB4395300ADC026 /* SearchSplitScreen.swift */; };
|
||||
7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC76D7A270083AE0084DB27 /* TextView.swift */; };
|
||||
7AC8B2762D6A01C700190706 /* UISearchTextField+Dumb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */; };
|
||||
7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91D2CB9B155005A5168 /* Mockable */; };
|
||||
@ -305,7 +307,6 @@
|
||||
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; };
|
||||
7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; };
|
||||
7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; };
|
||||
7A386A3F2DABDC190051676A /* MapScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapScreen.swift; sourceTree = "<group>"; };
|
||||
7A386A432DABDC360051676A /* MapViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModel.swift; sourceTree = "<group>"; };
|
||||
7A386A472DABE0D00051676A /* MapMarkerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapMarkerModel.swift; sourceTree = "<group>"; };
|
||||
@ -433,11 +434,14 @@
|
||||
7ABDA80C2D8721B10083C715 /* Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Substrings.swift; sourceTree = "<group>"; };
|
||||
7ABDA80E2D8723F90083C715 /* StorageService+AudioRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageService+AudioRecords.swift"; sourceTree = "<group>"; };
|
||||
7AC3554F29696D5A00889457 /* NewNumberController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewNumberController.swift; sourceTree = "<group>"; };
|
||||
7AC3555129696E3F00889457 /* UIView+layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+layout.swift"; sourceTree = "<group>"; };
|
||||
7AC35553296973E100889457 /* ACButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACButton.swift; sourceTree = "<group>"; };
|
||||
7AC355582969746600889457 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
|
||||
7AC3555A296995B200889457 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
|
||||
7AC44B812DB390B900ADC026 /* MainTabScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabScreen.swift; sourceTree = "<group>"; };
|
||||
7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySplitScreen.swift; sourceTree = "<group>"; };
|
||||
7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideTabBarModifier.swift; sourceTree = "<group>"; };
|
||||
7AC44B872DB438F200ADC026 /* UIView+layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+layout.swift"; sourceTree = "<group>"; };
|
||||
7AC44B892DB4395300ADC026 /* SearchSplitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSplitScreen.swift; sourceTree = "<group>"; };
|
||||
7AC76D7A270083AE0084DB27 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
|
||||
7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchTextField+Dumb.swift"; sourceTree = "<group>"; };
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
|
||||
@ -676,6 +680,7 @@
|
||||
7A131FD12D37B74100DC7755 /* HistoryScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */,
|
||||
7A131FD22D37B75500DC7755 /* HistoryScreen.swift */,
|
||||
7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */,
|
||||
7A4955812D58CCF900912E66 /* HistoryFilter.swift */,
|
||||
@ -789,6 +794,7 @@
|
||||
7A5911EC2D63225500EC51BA /* SearchScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC44B892DB4395300ADC026 /* SearchSplitScreen.swift */,
|
||||
7A5911ED2D63226F00EC51BA /* SearchScreen.swift */,
|
||||
7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */,
|
||||
);
|
||||
@ -1069,13 +1075,12 @@
|
||||
7AC355572969744100889457 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A3399AA299063370087DF98 /* SearchControllerExt.swift */,
|
||||
7AC3555129696E3F00889457 /* UIView+layout.swift */,
|
||||
7AC355582969746600889457 /* UIControl.swift */,
|
||||
7AC3555A296995B200889457 /* UIEdgeInsets.swift */,
|
||||
7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */,
|
||||
7A17CE492A2E820300626A6E /* UIStackView.swift */,
|
||||
7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */,
|
||||
7AC44B872DB438F200ADC026 /* UIView+layout.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -1169,6 +1174,7 @@
|
||||
7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */,
|
||||
7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */,
|
||||
7AF231982DA27C1B00AE5EB3 /* ACButtonView.swift */,
|
||||
7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */,
|
||||
);
|
||||
path = SwiftUI;
|
||||
sourceTree = "<group>";
|
||||
@ -1408,11 +1414,11 @@
|
||||
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */,
|
||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
||||
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */,
|
||||
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */,
|
||||
7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */,
|
||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
||||
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */,
|
||||
7AFBE8C02C3024E5003C491D /* ACHud.swift in Sources */,
|
||||
7AC44B8A2DB4395300ADC026 /* SearchSplitScreen.swift in Sources */,
|
||||
7AAAFADA2C4D1AFE0050410D /* Zoomable.swift in Sources */,
|
||||
7ADFC9572DAD0288001A43E3 /* GoogleAuthScreen.swift in Sources */,
|
||||
7AC8B2762D6A01C700190706 /* UISearchTextField+Dumb.swift in Sources */,
|
||||
@ -1430,12 +1436,14 @@
|
||||
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
|
||||
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
|
||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */,
|
||||
7AC44B882DB438F200ADC026 /* UIView+layout.swift in Sources */,
|
||||
7A6C65222D999325001240C2 /* AudioRecordViewModel.swift in Sources */,
|
||||
7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */,
|
||||
7AB4E42C2D397D8E0006D052 /* VehicleCellView.swift in Sources */,
|
||||
7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */,
|
||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */,
|
||||
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
|
||||
7AC44B842DB3EF7A00ADC026 /* HistorySplitScreen.swift in Sources */,
|
||||
7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */,
|
||||
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */,
|
||||
7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */,
|
||||
@ -1471,7 +1479,6 @@
|
||||
7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */,
|
||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */,
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */,
|
||||
7A9519822D80B6E500E69883 /* RecordsViewModel.swift in Sources */,
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */,
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||
@ -1495,6 +1502,7 @@
|
||||
7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */,
|
||||
7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */,
|
||||
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
||||
7AC44B862DB40A5C00ADC026 /* HideTabBarModifier.swift in Sources */,
|
||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
||||
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
|
||||
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
//
|
||||
// SearchControllerExt.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 06.02.2023.
|
||||
// Copyright © 2023 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UISearchController {
|
||||
|
||||
static var `default`: UISearchController {
|
||||
let searchController = UISearchController(searchResultsController: nil)
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
searchController.searchBar.keyboardType = .webSearch
|
||||
|
||||
if #available(iOS 16, *) {
|
||||
searchController.scopeBarActivation = .onTextEntry
|
||||
} else {
|
||||
searchController.automaticallyShowsScopeBar = true
|
||||
}
|
||||
|
||||
return searchController
|
||||
}
|
||||
|
||||
func placeholder(_ placeholder: String) -> UISearchController {
|
||||
searchBar.placeholder = placeholder
|
||||
return self
|
||||
}
|
||||
|
||||
func resultsUpdater(_ updater: UISearchResultsUpdating) -> UISearchController {
|
||||
searchResultsUpdater = updater
|
||||
return self
|
||||
}
|
||||
|
||||
func makeDumb() -> UISearchController {
|
||||
searchBar.autocorrectionType = .no
|
||||
searchBar.spellCheckingType = .no
|
||||
searchBar.autocapitalizationType = .none
|
||||
searchBar.smartDashesType = .no
|
||||
searchBar.smartQuotesType = .no
|
||||
searchBar.smartInsertDeleteType = .no
|
||||
return self
|
||||
}
|
||||
|
||||
func scopeButtons(_ buttons: [String]) -> UISearchController {
|
||||
searchBar.scopeButtonTitles = buttons
|
||||
return self
|
||||
}
|
||||
|
||||
func searchBarDelegate(_ delegate: UISearchBarDelegate) -> UISearchController {
|
||||
searchBar.delegate = delegate
|
||||
return self
|
||||
}
|
||||
}
|
||||
@ -10,23 +10,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
HUD.dimsBackground = true
|
||||
HUD.allowsInteraction = false
|
||||
|
||||
setupAppearance()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func setupAppearance() {
|
||||
|
||||
let tabBarAppearance = UITabBarAppearance()
|
||||
tabBarAppearance.configureWithDefaultBackground()
|
||||
UITabBar.appearance().standardAppearance = tabBarAppearance
|
||||
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
|
||||
|
||||
let navigationBarAppearance = UINavigationBarAppearance()
|
||||
navigationBarAppearance.configureWithDefaultBackground()
|
||||
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
|
||||
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
@ -42,30 +27,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
|
||||
// MARK: - Global menu
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
|
||||
override func buildMenu(with builder: UIMenuBuilder) {
|
||||
super.buildMenu(with: builder)
|
||||
|
||||
let command = UIKeyCommand(title: NSLocalizedString("Add new number", comment: ""),
|
||||
action: #selector(openAddNumberPanel),
|
||||
input: "n",
|
||||
modifierFlags: .command)
|
||||
let menu = UIMenu(options: .displayInline, children: [command])
|
||||
builder.insertChild(menu, atEndOfMenu: .file)
|
||||
}
|
||||
|
||||
@objc func openAddNumberPanel() {
|
||||
guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate else {
|
||||
return
|
||||
}
|
||||
|
||||
sceneDelegate.showAddNumberPanel()
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -97,21 +97,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
|
||||
let toolbar = NSToolbar(identifier: "main")
|
||||
toolbar.delegate = self
|
||||
toolbar.displayMode = .iconOnly
|
||||
|
||||
if let titlebar = windowScene.titlebar {
|
||||
titlebar.toolbar = toolbar
|
||||
if #available(macCatalyst 14.0, *) {
|
||||
titlebar.toolbarStyle = .unifiedCompact
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
self.window?.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
@ -180,74 +165,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
|
||||
extension NSToolbarItem.Identifier {
|
||||
|
||||
static let addNumber = NSToolbarItem.Identifier("pro.aliencat.add.number")
|
||||
}
|
||||
|
||||
extension SceneDelegate: NSToolbarDelegate {
|
||||
|
||||
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
let identifiers: [NSToolbarItem.Identifier] = [
|
||||
.addNumber
|
||||
]
|
||||
return identifiers
|
||||
}
|
||||
|
||||
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
return toolbarDefaultItemIdentifiers(toolbar)
|
||||
}
|
||||
|
||||
func toolbar(_ toolbar: NSToolbar,
|
||||
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
|
||||
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||
|
||||
var toolbarItem: NSToolbarItem?
|
||||
|
||||
switch itemIdentifier {
|
||||
case .addNumber:
|
||||
let item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||
item.image = UIImage(systemName: "plus")
|
||||
item.label = "Add"
|
||||
item.target = self
|
||||
item.action = #selector(showAddNumberPanel)
|
||||
toolbarItem = item
|
||||
|
||||
default:
|
||||
toolbarItem = nil
|
||||
}
|
||||
|
||||
return toolbarItem
|
||||
}
|
||||
|
||||
@objc func showAddNumberPanel() {
|
||||
let controller = NewNumberController()
|
||||
controller.preferredContentSize = CGSize(width: 400, height: 350)
|
||||
controller.onCheck = { number in
|
||||
controller.dismiss(animated: true) { [weak self] in
|
||||
self?.checkNewNumber(number)
|
||||
}
|
||||
}
|
||||
|
||||
self.window?.rootViewController?.present(controller, animated: true)
|
||||
}
|
||||
|
||||
func checkNewNumber(_ number: String) {
|
||||
guard let split = self.window?.rootViewController as? MainSplitController,
|
||||
let tabvc = split.viewControllers.first as? MainTabController
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
tabvc.selectedIndex = 0
|
||||
|
||||
Task {
|
||||
await tabvc.historyViewModel?.checkNewNumber(number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,6 +20,7 @@ enum DetailScreen: Hashable {
|
||||
struct FiltersScreen: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||
|
||||
@State var viewModel: FiltersViewModel
|
||||
|
||||
@ -113,6 +114,7 @@ struct FiltersScreen: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Filters")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Done") {
|
||||
@ -120,10 +122,18 @@ struct FiltersScreen: View {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
if horizontalSizeClass == .regular {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Close") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await viewModel.loadData()
|
||||
}
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
.hideTabBar()
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,91 +15,65 @@ struct HistoryScreen: View {
|
||||
@State var filterSheetPresented = false
|
||||
@State var exportSheetPresented = false
|
||||
|
||||
init() {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
self.viewModel = HistoryViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
}
|
||||
|
||||
init(viewModel: HistoryViewModel) {
|
||||
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
ForEach(viewModel.vehicleSections) { section in
|
||||
Section(header: Text(section.header)) {
|
||||
ForEach(section.elements) { vehicle in
|
||||
NavigationLink(value: vehicle) {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
List(selection: $viewModel.selectedVehicleId) {
|
||||
ForEach(viewModel.vehicleSections) { section in
|
||||
Section(header: Text(section.header)) {
|
||||
ForEach(section.elements) { vehicle in
|
||||
NavigationLink(value: vehicle.id) {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.listStyle(.plain)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||
viewModel.vehiclesCount))
|
||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||
.autocorrectionDisabled()
|
||||
.textInputAutocapitalization(.never)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("", systemImage: "square.and.arrow.up") {
|
||||
exportSheetPresented = true
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("", systemImage: "line.horizontal.3.decrease") {
|
||||
filterSheetPresented = true
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.listStyle(.plain)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||
viewModel.vehiclesCount))
|
||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||
.autocorrectionDisabled()
|
||||
.textInputAutocapitalization(.never)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("", systemImage: "square.and.arrow.up") {
|
||||
exportSheetPresented = true
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Filter check history", isPresented: $filterSheetPresented, titleVisibility: .visible) {
|
||||
ForEach(HistoryFilter.allCases) { filter in
|
||||
Button(filter.title) {
|
||||
viewModel.filter = filter
|
||||
viewModel.applyFilters()
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("", systemImage: "line.horizontal.3.decrease") {
|
||||
filterSheetPresented = true
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Export history as", isPresented: $exportSheetPresented, titleVisibility: .visible) {
|
||||
|
||||
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName)) {
|
||||
Text("CSV table")
|
||||
}
|
||||
|
||||
if let dbUrl = viewModel.dbFileURL {
|
||||
ShareLink(item: dbUrl) {
|
||||
Text("Database file")
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Filter check history", isPresented: $filterSheetPresented, titleVisibility: .visible) {
|
||||
ForEach(HistoryFilter.allCases) { filter in
|
||||
Button(filter.title) {
|
||||
viewModel.filter = filter
|
||||
viewModel.applyFilters()
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: VehicleDto.self) { vehicle in
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: true,
|
||||
onUpdate: viewModel.onVehicleChanged
|
||||
)
|
||||
}
|
||||
.confirmationDialog("Export history as", isPresented: $exportSheetPresented, titleVisibility: .visible) {
|
||||
|
||||
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName)) {
|
||||
Text("CSV table")
|
||||
}
|
||||
.navigationDestination(item: $viewModel.vehicleToOpen) { vehicle in
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: true,
|
||||
onUpdate: viewModel.onVehicleChanged
|
||||
)
|
||||
|
||||
if let dbUrl = viewModel.dbFileURL {
|
||||
ShareLink(item: dbUrl) {
|
||||
Text("Database file")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
AutoCat/Screens/HistoryScreen/HistorySplitScreen.swift
Normal file
29
AutoCat/Screens/HistoryScreen/HistorySplitScreen.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// HistorySplitScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct HistorySplitScreen: View {
|
||||
|
||||
var viewModel: HistoryViewModel
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
HistoryScreen(viewModel: viewModel)
|
||||
} detail: {
|
||||
if let vehicle = viewModel.selectedVehicle {
|
||||
ReportScreen(vehicle: vehicle, isPersistent: true, onUpdate: viewModel.onVehicleChanged)
|
||||
.id(vehicle.id)
|
||||
} else {
|
||||
Text("No vehicle selected")
|
||||
}
|
||||
}
|
||||
.navigationSplitViewStyle(.balanced)
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,19 @@ final class HistoryViewModel: ACHudContainer {
|
||||
var vehiclesFiltered: [VehicleDto] = []
|
||||
var vehicleSections: [DateSection<VehicleDto>] = []
|
||||
|
||||
var vehicleToOpen: VehicleDto?
|
||||
var selectedVehicleId: VehicleDto.ID?
|
||||
|
||||
var selectedVehicle: VehicleDto? {
|
||||
guard let id = selectedVehicleId else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let vehicle = vehicles.first(where: { $0.id == id }) {
|
||||
return vehicle
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var searchText: String = "" {
|
||||
didSet {
|
||||
@ -130,7 +142,7 @@ final class HistoryViewModel: ACHudContainer {
|
||||
hud = nil
|
||||
if !vehicle.unrecognized {
|
||||
// TODO: Fix programmatic navigation
|
||||
//vehicleToOpen = vehicle
|
||||
//selectedVehicleId = vehicle.id
|
||||
}
|
||||
} else {
|
||||
showErrors(errors)
|
||||
|
||||
@ -38,7 +38,7 @@ struct MainTabScreen: View {
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $selection) {
|
||||
HistoryScreen(viewModel: historyViewModel)
|
||||
HistorySplitScreen(viewModel: historyViewModel)
|
||||
.tabItem {
|
||||
Label("History", systemImage: "clock.arrow.circlepath")
|
||||
}
|
||||
@ -58,7 +58,7 @@ struct MainTabScreen: View {
|
||||
.tag(2)
|
||||
#endif
|
||||
|
||||
SearchScreen()
|
||||
SearchSplitScreen()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
@ -115,7 +115,3 @@ struct MainTabScreen: View {
|
||||
Task { await historyViewModel.checkRecord(number: number, event: event) }
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainTabScreen()
|
||||
}
|
||||
|
||||
@ -13,6 +13,9 @@ import AutoCatCore
|
||||
|
||||
struct MapScreen: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||
|
||||
@State var viewModel: MapViewModel
|
||||
|
||||
init(mapInput: MapInput) {
|
||||
@ -37,6 +40,7 @@ struct MapScreen: View {
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.navigationTitle(viewModel.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.readSize { newValue in
|
||||
viewModel.mapSize = newValue
|
||||
}
|
||||
@ -48,6 +52,15 @@ struct MapScreen: View {
|
||||
await viewModel.reloadMarkers()
|
||||
}
|
||||
}
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
.toolbar {
|
||||
if horizontalSizeClass == .regular {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button("Close") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.hideTabBar()
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,116 +37,118 @@ struct ReportScreen: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent {
|
||||
Text(viewModel.vehicle.brand?.name?.original ?? "")
|
||||
} label: {
|
||||
AsyncImage(url: URL(string: viewModel.vehicle.brand?.logo ?? "")) { phase in
|
||||
phase.image?
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent {
|
||||
Text(viewModel.vehicle.brand?.name?.original ?? "")
|
||||
} label: {
|
||||
AsyncImage(url: URL(string: viewModel.vehicle.brand?.logo ?? "")) { phase in
|
||||
phase.image?
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("General") {
|
||||
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.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") {
|
||||
NavigationLink(value: Screen.events) {
|
||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||
}
|
||||
|
||||
NavigationLink(value: Screen.osago) {
|
||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.osagoContracts.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.owners) {
|
||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ownershipPeriods.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.photos) {
|
||||
LabeledContent("Photos", value: String(viewModel.vehicle.photos.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.photos.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.ads) {
|
||||
LabeledContent("Ads", value: String(viewModel.vehicle.ads.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ads.isEmpty)
|
||||
|
||||
|
||||
NavigationLink(value: Screen.notes) {
|
||||
LabeledContent("Notes", value: String(viewModel.vehicle.notes.count))
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.showDebugInfo {
|
||||
Section("Debug info") {
|
||||
makeDebugInfoCell(title: "Avtocod", model: viewModel.vehicle.debugInfo?.autocod)
|
||||
makeDebugInfoCell(title: "Vin01 (VIN)", model: viewModel.vehicle.debugInfo?.vin01vin)
|
||||
makeDebugInfoCell(title: "Vin01 (base)", model: viewModel.vehicle.debugInfo?.vin01base)
|
||||
makeDebugInfoCell(title: "Vin01 (history)", model: viewModel.vehicle.debugInfo?.vin01history)
|
||||
makeDebugInfoCell(title: "Nomerogram", model: viewModel.vehicle.debugInfo?.nomerogram)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button("Check GB") {
|
||||
Task { await viewModel.checkGB() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("General") {
|
||||
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.steerignWheelPosition)
|
||||
LabeledContent("Japanese", value: viewModel.isJapanese)
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
|
||||
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") {
|
||||
NavigationLink(value: Screen.events) {
|
||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||
}
|
||||
|
||||
NavigationLink(value: Screen.osago) {
|
||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.osagoContracts.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.owners) {
|
||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ownershipPeriods.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.photos) {
|
||||
LabeledContent("Photos", value: String(viewModel.vehicle.photos.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.photos.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.ads) {
|
||||
LabeledContent("Ads", value: String(viewModel.vehicle.ads.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ads.isEmpty)
|
||||
|
||||
|
||||
NavigationLink(value: Screen.notes) {
|
||||
LabeledContent("Notes", value: String(viewModel.vehicle.notes.count))
|
||||
.hud($viewModel.hud)
|
||||
.toolbar {
|
||||
if let link = viewModel.shareLink {
|
||||
ShareLink(item: link)
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.showDebugInfo {
|
||||
Section("Debug info") {
|
||||
makeDebugInfoCell(title: "Avtocod", model: viewModel.vehicle.debugInfo?.autocod)
|
||||
makeDebugInfoCell(title: "Vin01 (VIN)", model: viewModel.vehicle.debugInfo?.vin01vin)
|
||||
makeDebugInfoCell(title: "Vin01 (base)", model: viewModel.vehicle.debugInfo?.vin01base)
|
||||
makeDebugInfoCell(title: "Vin01 (history)", model: viewModel.vehicle.debugInfo?.vin01history)
|
||||
makeDebugInfoCell(title: "Nomerogram", model: viewModel.vehicle.debugInfo?.nomerogram)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button("Check GB") {
|
||||
Task { await viewModel.checkGB() }
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .events:
|
||||
EventsScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .osago:
|
||||
OsagoScreen(contracts: viewModel.vehicle.osagoContracts)
|
||||
case .owners:
|
||||
OwnersScreen(ownerships: viewModel.vehicle.ownershipPeriods)
|
||||
case .notes:
|
||||
NotesScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .ads:
|
||||
AdsScreen(ads: viewModel.vehicle.ads)
|
||||
case .photos:
|
||||
GalleryScreen(photos: viewModel.vehicle.photos)
|
||||
}
|
||||
}
|
||||
.hideTabBar()
|
||||
}
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.toolbar {
|
||||
if let link = viewModel.shareLink {
|
||||
ShareLink(item: link)
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .events:
|
||||
EventsScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .osago:
|
||||
OsagoScreen(contracts: viewModel.vehicle.osagoContracts)
|
||||
case .owners:
|
||||
OwnersScreen(ownerships: viewModel.vehicle.ownershipPeriods)
|
||||
case .notes:
|
||||
NotesScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .ads:
|
||||
AdsScreen(ads: viewModel.vehicle.ads)
|
||||
case .photos:
|
||||
GalleryScreen(photos: viewModel.vehicle.photos)
|
||||
}
|
||||
}
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
@ -13,63 +13,50 @@ struct SearchScreen: View {
|
||||
|
||||
enum Screen: Hashable {
|
||||
|
||||
case report(VehicleDto)
|
||||
case filter(Filter)
|
||||
case map(Filter)
|
||||
}
|
||||
|
||||
@State var viewModel: SearchViewModel
|
||||
|
||||
init() {
|
||||
init(viewModel: SearchViewModel) {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
self.viewModel = SearchViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
vehicles
|
||||
if viewModel.hasMoreData && !viewModel.vehicleSections.isEmpty {
|
||||
progressCell
|
||||
}
|
||||
List(selection: $viewModel.selectedVehicleId) {
|
||||
vehicles
|
||||
if viewModel.hasMoreData && !viewModel.vehicleSections.isEmpty {
|
||||
progressCell
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.hud($viewModel.hud)
|
||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||
viewModel.vehiclesCount))
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.hud($viewModel.hud)
|
||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||
viewModel.vehiclesCount))
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.refreshable {
|
||||
Task { await viewModel.reloadData() }
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
toolbarMenu
|
||||
}
|
||||
.refreshable {
|
||||
Task { await viewModel.reloadData() }
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
toolbarMenu
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .report(let vehicle):
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: false,
|
||||
onUpdate: viewModel.onVehicleChanged
|
||||
)
|
||||
case .filter(let filter):
|
||||
FiltersScreen(filter: filter, onUpdate: viewModel.onFilterChanged)
|
||||
case .map(let filter):
|
||||
MapScreen(mapInput: .filter(filter))
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .filter(let filter):
|
||||
FiltersScreen(filter: filter, onUpdate: viewModel.onFilterChanged)
|
||||
case .map(let filter):
|
||||
MapScreen(mapInput: .filter(filter))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,7 +65,7 @@ struct SearchScreen: View {
|
||||
ForEach(viewModel.vehicleSections) { section in
|
||||
Section(header: Text(section.header)) {
|
||||
ForEach(section.elements) { vehicle in
|
||||
NavigationLink(value: Screen.report(vehicle)) {
|
||||
NavigationLink(value: vehicle.id) {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
37
AutoCat/Screens/SearchScreen/SearchSplitScreen.swift
Normal file
37
AutoCat/Screens/SearchScreen/SearchSplitScreen.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// SearchSplitScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct SearchSplitScreen: View {
|
||||
|
||||
@State var viewModel: SearchViewModel
|
||||
|
||||
init() {
|
||||
let resolver = ServiceContainer.shared
|
||||
self.viewModel = SearchViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
SearchScreen(viewModel: viewModel)
|
||||
} detail: {
|
||||
if let vehicle = viewModel.selectedVehicle {
|
||||
ReportScreen(vehicle: vehicle, isPersistent: false, onUpdate: viewModel.onVehicleChanged)
|
||||
.id(vehicle.id)
|
||||
} else {
|
||||
Text("No vehicle selected")
|
||||
}
|
||||
}
|
||||
.navigationSplitViewStyle(.balanced)
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,20 @@ final class SearchViewModel: ACHudContainer {
|
||||
var vehicles: [VehicleDto] = []
|
||||
var vehicleSections: [DateSection<VehicleDto>] = []
|
||||
|
||||
var selectedVehicleId: VehicleDto.ID?
|
||||
|
||||
var selectedVehicle: VehicleDto? {
|
||||
guard let id = selectedVehicleId else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let vehicle = vehicles.first(where: { $0.id == id }) {
|
||||
return vehicle
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var filter = Filter()
|
||||
var pageToken: String?
|
||||
var hasMoreData: Bool = true
|
||||
|
||||
32
AutoCat/SwiftUI/HideTabBarModifier.swift
Normal file
32
AutoCat/SwiftUI/HideTabBarModifier.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// HideTabBarModifier.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct HideTabBarModifier: ViewModifier {
|
||||
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
|
||||
if horizontalSizeClass == .compact {
|
||||
content
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
public func hideTabBar() -> some View {
|
||||
|
||||
modifier(HideTabBarModifier())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user