Adding list of vehicles

This commit is contained in:
Selim Mustafaev 2022-09-12 00:02:11 +03:00
parent 7d1811171e
commit d90fbfc5f0
21 changed files with 729 additions and 139 deletions

View File

@ -18,11 +18,14 @@
6841A8FF53F0AADF96B138C1 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AFE790F6FC06838B1E2C /* UIControl.swift */; };
6841ABD5E4B126DEF3612BBD /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AB0052E9DB6914901EA3 /* PNKeyboard.swift */; };
6841AF924E165F1B3A3B5FB5 /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841ABEA0314E3B4E438C311 /* AuthController.swift */; };
7A024AAE28BD5B450010D3D1 /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A024AAC28BD5B450010D3D1 /* RoadNumbers.otf */; };
7A024AAF28BD5B450010D3D1 /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A024AAD28BD5B450010D3D1 /* RoadNumbers2.0.otf */; };
7A0391D6285933EF000EE522 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; };
7A0391D7285933EF000EE522 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7A0391DD28593DBC000EE522 /* SidebarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0391DC28593DBC000EE522 /* SidebarController.swift */; };
7A054FD928C4BE560002C386 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A054FD828C4BE560002C386 /* PlateView.swift */; };
7A054FDA28C4C24B0002C386 /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A24C19E27EE26B900049E7F /* CenterTextLayer.swift */; };
7A163BF128BBE9ED0005A0A4 /* VehiclesListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A163BF028BBE9ED0005A0A4 /* VehiclesListView.swift */; };
7A163BF328BBEAFC0005A0A4 /* PlateNumberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A163BF228BBEAFC0005A0A4 /* PlateNumberView.swift */; };
7A1D80E027F1F275007BD64F /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1D80DF27F1F275007BD64F /* DifferenceKit */; };
7A1D80E627F20FCB007BD64F /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1D80E527F20FCB007BD64F /* DifferenceKit */; };
7A1D80E827F30399007BD64F /* VModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1D80E727F30399007BD64F /* VModel.swift */; };
@ -87,6 +90,7 @@
7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */; };
7A558AB027FA3CCF001A18EE /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA527FA3CCF001A18EE /* SettingsTests.swift */; };
7A558AB127FA3CCF001A18EE /* ApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA627FA3CCF001A18EE /* ApiTests.swift */; };
7A9471BD28C52DD80054CB0A /* PlateViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9471BC28C52DD80054CB0A /* PlateViewItem.swift */; };
7A9F2AC327E71531006492A9 /* ACTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */; };
7A9FD4072857AF590057ECFA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FD4062857AF590057ECFA /* AppDelegate.swift */; };
7A9FD4092857AF590057ECFA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FD4082857AF590057ECFA /* ViewController.swift */; };
@ -100,6 +104,7 @@
7AE32D6927F06536004EF6E0 /* CoreDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE32D6827F06536004EF6E0 /* CoreDataSource.swift */; };
7AE32D6E27F06D2D004EF6E0 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AE32D6D27F06D2D004EF6E0 /* SwiftDate */; };
7AE32D7127F06DA4004EF6E0 /* DateSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE32D7027F06DA4004EF6E0 /* DateSection.swift */; };
7AE9F20428CDFCC600ABF6DF /* VehicleDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE9F20328CDFCC600ABF6DF /* VehicleDetailView.swift */; };
7AF1D0D828BB577E004E19F7 /* CheckNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1D0D728BB577E004E19F7 /* CheckNumber.swift */; };
7AF1D0DA28BB5BF5004E19F7 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1D0D928BB5BF5004E19F7 /* View.swift */; };
7AFD7AE02871823E00BCCD37 /* SidebarSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFD7ADF2871823E00BCCD37 /* SidebarSection.swift */; };
@ -220,9 +225,12 @@
6841ADEEC165FA9124C5EF40 /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = "<group>"; };
6841AFB465BF16E122875D9A /* ACButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACButton.swift; sourceTree = "<group>"; };
6841AFE790F6FC06838B1E2C /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
7A024AAC28BD5B450010D3D1 /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
7A024AAD28BD5B450010D3D1 /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = "<group>"; };
7A024AB028BD5BB30010D3D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
7A0391DC28593DBC000EE522 /* SidebarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarController.swift; sourceTree = "<group>"; };
7A054FD828C4BE560002C386 /* PlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = "<group>"; };
7A163BF028BBE9ED0005A0A4 /* VehiclesListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclesListView.swift; sourceTree = "<group>"; };
7A163BF228BBEAFC0005A0A4 /* PlateNumberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumberView.swift; sourceTree = "<group>"; };
7A1D80E727F30399007BD64F /* VModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VModel.swift; sourceTree = "<group>"; };
7A24C19527EE212E00049E7F /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
7A24C19627EE212E00049E7F /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = "<group>"; };
@ -286,6 +294,7 @@
7A558AAC27FA3CCF001A18EE /* login_success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = login_success.json; sourceTree = "<group>"; };
7A558AAE27FA3CCF001A18EE /* MockURLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = "<group>"; };
7A558AAF27FA3CCF001A18EE /* ApiMethodMockProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiMethodMockProtocol.swift; sourceTree = "<group>"; };
7A9471BC28C52DD80054CB0A /* PlateViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateViewItem.swift; sourceTree = "<group>"; };
7A9F2AC227E71531006492A9 /* ACTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACTabBarController.swift; sourceTree = "<group>"; };
7A9FD4042857AF590057ECFA /* AutoCat2Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2Mac.app; sourceTree = BUILT_PRODUCTS_DIR; };
7A9FD4062857AF590057ECFA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -299,6 +308,7 @@
7AE32D6527F063A1004EF6E0 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
7AE32D6827F06536004EF6E0 /* CoreDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataSource.swift; sourceTree = "<group>"; };
7AE32D7027F06DA4004EF6E0 /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = "<group>"; };
7AE9F20328CDFCC600ABF6DF /* VehicleDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleDetailView.swift; sourceTree = "<group>"; };
7AF1D0D728BB577E004E19F7 /* CheckNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckNumber.swift; sourceTree = "<group>"; };
7AF1D0D928BB5BF5004E19F7 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
7AFD7ADF2871823E00BCCD37 /* SidebarSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSection.swift; sourceTree = "<group>"; };
@ -431,6 +441,15 @@
path = Components;
sourceTree = "<group>";
};
7A024AAB28BD5B450010D3D1 /* Fonts */ = {
isa = PBXGroup;
children = (
7A024AAC28BD5B450010D3D1 /* RoadNumbers.otf */,
7A024AAD28BD5B450010D3D1 /* RoadNumbers2.0.otf */,
);
path = Fonts;
sourceTree = "<group>";
};
7A0391DB28593DAB000EE522 /* Controllers */ = {
isa = PBXGroup;
children = (
@ -441,6 +460,15 @@
path = Controllers;
sourceTree = "<group>";
};
7A054FD728C4BB700002C386 /* PlateView */ = {
isa = PBXGroup;
children = (
7A054FD828C4BE560002C386 /* PlateView.swift */,
7A9471BC28C52DD80054CB0A /* PlateViewItem.swift */,
);
path = PlateView;
sourceTree = "<group>";
};
7A163BEF28BBE9DA0005A0A4 /* VehiclesList */ = {
isa = PBXGroup;
children = (
@ -498,6 +526,8 @@
7A4951B5288D3A6100C644B6 /* AutoCat2SUI */ = {
isa = PBXGroup;
children = (
7A024AB028BD5BB30010D3D1 /* Info.plist */,
7A024AAB28BD5B450010D3D1 /* Fonts */,
7A4951B6288D3A6100C644B6 /* AutoCat2SUIApp.swift */,
7A4951D6288D5EE700C644B6 /* Extensions */,
7A4951CF288D5C2100C644B6 /* Views */,
@ -520,6 +550,7 @@
7A4951C3288D3AF000C644B6 /* Screens */ = {
isa = PBXGroup;
children = (
7AE9F20228CDFCAB00ABF6DF /* VehicleDetail */,
7A163BEF28BBE9DA0005A0A4 /* VehiclesList */,
7AF1D0D628BB5768004E19F7 /* CheckNumber */,
7A4951CE288D5C1300C644B6 /* Main */,
@ -548,9 +579,9 @@
7A4951CF288D5C2100C644B6 /* Views */ = {
isa = PBXGroup;
children = (
7A054FD728C4BB700002C386 /* PlateView */,
7A4951B8288D3A6100C644B6 /* RootView.swift */,
7A4951D0288D5C4300C644B6 /* ACProgressView.swift */,
7A163BF228BBEAFC0005A0A4 /* PlateNumberView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -778,6 +809,14 @@
path = DataSource;
sourceTree = "<group>";
};
7AE9F20228CDFCAB00ABF6DF /* VehicleDetail */ = {
isa = PBXGroup;
children = (
7AE9F20328CDFCC600ABF6DF /* VehicleDetailView.swift */,
);
path = VehicleDetail;
sourceTree = "<group>";
};
7AF1D0D628BB5768004E19F7 /* CheckNumber */ = {
isa = PBXGroup;
children = (
@ -1054,6 +1093,8 @@
files = (
7A4951BF288D3A6300C644B6 /* Preview Assets.xcassets in Resources */,
7A4951BB288D3A6300C644B6 /* Assets.xcassets in Resources */,
7A024AAE28BD5B450010D3D1 /* RoadNumbers.otf in Resources */,
7A024AAF28BD5B450010D3D1 /* RoadNumbers2.0.otf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1116,13 +1157,16 @@
buildActionMask = 2147483647;
files = (
7A4951D3288D5E2800C644B6 /* AuthVM.swift in Sources */,
7A054FDA28C4C24B0002C386 /* CenterTextLayer.swift in Sources */,
7A163BF128BBE9ED0005A0A4 /* VehiclesListView.swift in Sources */,
7A054FD928C4BE560002C386 /* PlateView.swift in Sources */,
7A4951B9288D3A6100C644B6 /* RootView.swift in Sources */,
7A4951C7288D3BDD00C644B6 /* MainView.swift in Sources */,
7AE9F20428CDFCC600ABF6DF /* VehicleDetailView.swift in Sources */,
7AF1D0D828BB577E004E19F7 /* CheckNumber.swift in Sources */,
7A4951B7288D3A6100C644B6 /* AutoCat2SUIApp.swift in Sources */,
7A9471BD28C52DD80054CB0A /* PlateViewItem.swift in Sources */,
7AF1D0DA28BB5BF5004E19F7 /* View.swift in Sources */,
7A163BF328BBEAFC0005A0A4 /* PlateNumberView.swift in Sources */,
7A4951D1288D5C4300C644B6 /* ACProgressView.swift in Sources */,
7A4951C5288D3BCF00C644B6 /* AuthView.swift in Sources */,
7A4951D5288D5ED000C644B6 /* Alert.swift in Sources */,
@ -1328,6 +1372,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AutoCat2SUI/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat2;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
@ -1367,6 +1412,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AutoCat2SUI/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat2;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;

View File

@ -12,7 +12,7 @@
<key>AutoCat2.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
<key>AutoCat2Mac.xcscheme_^#shared#^_</key>
<dict>
@ -22,7 +22,7 @@
<key>AutoCat2SUI.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>0</integer>
</dict>
<key>AutoCat2UITests.testExample.xcscheme</key>
<dict>

View File

@ -26,13 +26,13 @@ class SidebarFilterItem: SidebarItem {
SidebarFilterItem(iconName: "internaldrive",
title: "Local history",
filter: Filter(dataSource: .local))
filter: Filter.allLocal)
}
static var remote: SidebarFilterItem {
SidebarFilterItem(iconName: "externaldrive.connected.to.line.below",
title: "Remote DB",
filter: Filter(dataSource: .remote))
filter: Filter.allRemote)
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,33 @@
{
"colors" : [
{
"color" : {
"platform" : "ios",
"reference" : "systemGray2Color"
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.290",
"green" : "0.282",
"red" : "0.282"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.300",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.152",
"green" : "0.152",
"red" : "0.152"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.182",
"green" : "0.225",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.866",
"green" : "0.866",
"red" : "0.866"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -6,11 +6,14 @@
//
import SwiftUI
import AppKit
import AutoCatCore
@main
struct AutoCat2SUIApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let storageService = StorageService.sharedNotWait
var body: some Scene {
@ -41,3 +44,24 @@ struct AutoCat2SUIApp: App {
return settings
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
true
}
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.keyWindow?.toolbar?.insertItem(withItemIdentifier: .sidebarTrackingSeparator, at: 2)
//NSApplication.shared.keyWindow
}
// func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
// if connectingSceneSession.role == .windowApplication {
// configuration.delegateClass = SceneDelegate.self
// }
// return configuration
// }
}

View File

@ -8,43 +8,60 @@
import SwiftUI
import AutoCatCore
struct SidebarSection: Identifiable {
let name: String
let filters: [Filter]
let id = UUID()
func contains(filter id: UUID) -> Bool {
filters.contains { $0.id == id }
}
}
struct MainView: View {
@FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<CDVehicle>
@State private var selectedFilter: Filter?
@State private var selectedFilterId: UUID?
@State var selectedVehicle: CDVehicle?
@State private var checkSheetPresented = false
private var historyFilters: [Filter] = [
@State var columnVisibility = NavigationSplitViewVisibility.all
private let sections: [SidebarSection] = [
SidebarSection(name: "History", filters: [
.allLocal,
.unrecognized,
.outdated
]),
SidebarSection(name: "Remote", filters: [
.allRemote
])
]
private var remoteFilters: [Filter] = [
.allRemote
]
var selectedFilter: Filter? {
guard let filterId = selectedFilterId else {
return nil
}
return sections.first(where: { $0.contains(filter: filterId) })?
.filters.first(where: { $0.id == filterId })
}
var body: some View {
NavigationSplitView {
List(selection: $selectedFilter) {
Section("History") {
ForEach(historyFilters) { filter in
NavigationLink(value: filter) {
Label(filter.name, systemImage: filter.iconName)
.badge(vehicles.count)
}
}
}
Section("Remote") {
ForEach(remoteFilters) { filter in
NavigationLink(value: filter) {
NavigationSplitView(columnVisibility: $columnVisibility) {
List(selection: $selectedFilterId) {
ForEach(sections) { section in
Section(section.name) {
ForEach(section.filters) { filter in
Label(filter.name, systemImage: filter.iconName)
.badge(vehicles.filter(filter.match).count)
}
}
}
}
.navigationSplitViewColumnWidth(180)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
Spacer()
@ -59,14 +76,42 @@ struct MainView: View {
}
}
.navigationDestination(for: Filter.self) { filter in
VehiclesListView(vehicles: [], selection: nil)
}
} content: {
Text("Content")
} detail: {
Text("Detail")
if let filter = selectedFilter {
let filtered = vehicles.filter(filter.match)
if filtered.isEmpty {
VStack {
Image(systemName: filter.iconName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 64)
.foregroundColor(.secondary)
Text("Nothing here")
.font(.system(size: 18))
.foregroundColor(.secondary)
}
} else {
VehiclesListView(vehicles: filtered, selection: $selectedVehicle)
.toolbar {
ToolbarItem {
Button(action: {}) {
Image(systemName: "trash")
}
}
}
}
} else {
EmptyView()
}
} detail: {
if let vehicle = selectedVehicle {
VehicleDetailView(vehicle: vehicle)
} else {
EmptyView()
}
}
.navigationSplitViewStyle(.balanced)
}
}

View File

@ -0,0 +1,63 @@
//
// VehicleDetailView.swift
// AutoCat2SUI
//
// Created by Selim Mustafaev on 11.09.2022.
//
import SwiftUI
import AutoCatCore
struct ReportTextItem: View {
let name: String
let value: String?
var body: some View {
HStack {
Text(name)
Spacer()
Text(value ?? "")
.foregroundColor(.secondary)
}
}
}
struct VehicleDetailView: View {
let vehicle: CDVehicle
var body: some View {
Form {
Section(header: Text("General")) {
ReportTextItem(name: "Year", value: String(vehicle.year))
ReportTextItem(name: "Color", value: vehicle.color)
ReportTextItem(name: "Category", value: vehicle.category)
ReportTextItem(name: "Steering wheel position", value: vehicle.isRightWheel ? "Right": "Left")
ReportTextItem(name: "Japanese", value: vehicle.isJapanese ? "Yes" : "No")
}
Section(header: Text("Identifiers")) {
ReportTextItem(name: "Plate number", value: vehicle.number)
ReportTextItem(name: "VIN", value: vehicle.vin1)
ReportTextItem(name: "STS", value: vehicle.sts)
ReportTextItem(name: "PTS", value: vehicle.pts)
}
}
.formStyle(.grouped)
}
}
struct VehicleDetailView_Previews: PreviewProvider {
static var previews: some View {
let test1 = Vehicle(number: "Н282СН61",
brandName: "KIA ОПТИМА",
addedDate: Date().timeIntervalSince1970,
updatedDate: Date().timeIntervalSince1970)
return VehicleDetailView(vehicle: CDVehicle(vehicle: test1,
context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)))
}
}

View File

@ -11,20 +11,23 @@ import AutoCatCore
struct VehiclesListView: View {
var vehicles: [CDVehicle]
@State var selection: CDVehicle?
@Binding var selection: CDVehicle?
var body: some View {
List(selection: $selection) {
ForEach(vehicles, id: \.self) { vehicle in
let number = PlateNumber(vehicle.number ?? "")
PlateNumberView(number: number, unrecognized: vehicle.unrecognized, outdated: vehicle.outdated)
PlateViewItem(vehicle: vehicle)
}
}
}
}
struct VehiclesListView_Previews: PreviewProvider {
@State var selection: CDVehicle?
static var previews: some View {
VehiclesListView(vehicles: [])
VehiclesListView(vehicles: [
], selection: .constant(nil))
}
}

View File

@ -1,68 +0,0 @@
//
// PlateNumberView.swift
// AutoCat2SUI
//
// Created by Selim Mustafaev on 28.08.2022.
//
import SwiftUI
import AutoCatCore
struct PlateNumberView: View {
let number: PlateNumber
let unrecognized: Bool
let outdated: Bool
private var fgColor: Color {
if unrecognized {
return Color("PlateBackgroundError")
} else {
return Color("PlateForeground")
}
}
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 6)
.fill(fgColor)
GeometryReader { geometry in
HStack(alignment: .center, spacing: 2) {
ZStack {
RoundedRectangle(cornerRadius: 4)
.fill(Color("PlateBackground"))
Text(number.mainPart())
.font(Font.custom("RoadNumbers", size: geometry.size.height*0.9))
}
.frame(width: geometry.size.width*0.73 - 1)
ZStack {
RoundedRectangle(cornerRadius: 4)
.fill(Color("PlateBackground"))
VStack(spacing: 0) {
Text(number.region())
.frame(height: geometry.size.height*0.65, alignment: .center)
HStack {
}
.frame(height: geometry.size.height*0.35, alignment: .center)
}
}
.frame(width: geometry.size.width*0.27 - 1)
}
}
.padding(2)
}
.aspectRatio(520.0/112.0, contentMode: .fit)
}
}
struct PlateNumberView_Previews: PreviewProvider {
static var previews: some View {
Group {
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: false)
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: true, outdated: false)
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: true)
}
.previewLayout(.fixed(width: 200, height: 50))
}
}

View File

@ -0,0 +1,232 @@
#if os(macOS)
import AppKit
import AutoCatCore
import SwiftUI
extension CGRect {
func inset(by insets: NSEdgeInsets) -> CGRect {
CGRect(origin: CGPoint(x: origin.x + insets.left,
y: origin.y + insets.bottom),
size: CGSize(width: size.width - insets.left - insets.right,
height: size.height - insets.top - insets.bottom))
}
}
extension NSScreen {
static var mainScreenScale: CGFloat {
main?.backingScaleFactor ?? 1
}
}
class FlagLayer: CALayer {
let pixelWidth = 1/NSScreen.mainScreenScale
// Flag colors - https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%B0%D0%B3_%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B8
let blue = NSColor(srgbRed: 0, green: 57/256.0, blue: 166/256.0, alpha: 1)
let red = NSColor(srgbRed: 213/256.0, green: 43/256.0, blue: 30/256.0, alpha: 1)
override func draw(in ctx: CGContext) {
ctx.saveGState()
super.draw(in: ctx)
ctx.setStrokeColor(NSColor.black.cgColor)
ctx.setLineWidth(0)
ctx.setFillColor(NSColor.white.cgColor)
ctx.fill(bounds.inset(by: NSEdgeInsets(top: 0, left: 0, bottom: bounds.height*2/3, right: 0)))
ctx.setFillColor(self.blue.cgColor)
ctx.fill(bounds.insetBy(dx: 0, dy: bounds.height/3))
ctx.setFillColor(self.red.cgColor)
ctx.fill(bounds.inset(by: NSEdgeInsets(top: bounds.height*2/3, left: 0, bottom: 0, right: 0)))
ctx.setLineWidth(pixelWidth)
ctx.stroke(bounds.insetBy(dx: pixelWidth/2, dy: pixelWidth/2))
ctx.restoreGState()
}
}
class PlateView: NSView {
// Some driver plate parameters from "ГОСТ Р 50577-93"
// http://docs.cntd.ru/document/gost-r-50577-93
private static let aspectRatio: CGFloat = 112.0/520.0
private static let fontHeightCoeff: CGFloat = 58.0/76.0
private var bgLayer = CALayer()
private var mainBgLayer = CALayer()
private var regionBgLayer = CALayer()
private var numberLayer = CenterTextLayer(coeff: fontHeightCoeff)
private var regionLayer = CenterTextLayer(coeff: fontHeightCoeff)
private var countryLayer = CenterTextLayer()
private var flagLayer = FlagLayer()
var number: PlateNumber? {
didSet {
self.layout()
self.setAccessibilityLabel(number?.asString())
}
}
var foreground: NSColor? {
didSet {
self.layout()
}
}
var fontSize: CGFloat = 10 {
didSet {
self.layout()
}
}
var onChange: (() -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.clear.cgColor
self.bgLayer.cornerRadius = 6 //self.bounds.height/8
self.layer?.addSublayer(self.bgLayer)
self.mainBgLayer.cornerRadius = 4
self.layer?.addSublayer(self.mainBgLayer)
self.regionBgLayer.cornerRadius = 4
self.layer?.addSublayer(self.regionBgLayer)
self.numberLayer.alignmentMode = .center
self.numberLayer.contentsScale = NSScreen.mainScreenScale
self.layer?.addSublayer(self.numberLayer)
self.regionLayer.alignmentMode = .center
self.regionLayer.contentsScale = NSScreen.mainScreenScale
self.layer?.addSublayer(self.regionLayer)
self.countryLayer.contentsScale = NSScreen.mainScreenScale
self.countryLayer.string = "RUS"
self.countryLayer.alignmentMode = .center
self.layer?.addSublayer(self.countryLayer)
self.flagLayer.contentsScale = NSScreen.mainScreenScale
self.layer?.addSublayer(self.flagLayer)
self.setAccessibilityElement(true)
}
private func pathForFlag(in rect: CGRect) -> CGPath {
let path = CGMutablePath()
let rect = CGPath(rect: rect, transform: nil)
path.addPath(rect)
return path
}
override func layout() {
super.layout()
guard let number = self.number else { return }
guard let fgColorMain = NSColor(named: "PlateForeground")?.cgColor else { return }
guard let bgColor = NSColor(named: "PlateBackground")?.cgColor else { return }
let fgColor = self.foreground?.cgColor ?? fgColorMain
self.bgLayer.backgroundColor = fgColor
self.bgLayer.frame = self.bounds
self.mainBgLayer.backgroundColor = bgColor
self.regionBgLayer.backgroundColor = bgColor
self.mainBgLayer.frame = self.bounds.inset(by: NSEdgeInsets(top: 2, left: 2, bottom: 2, right: self.bounds.width*0.27 + 1))
self.regionBgLayer.frame = self.bounds.inset(by: NSEdgeInsets(top: 2, left: self.bounds.width*0.73 + 1, bottom: 2, right: 2))
self.numberLayer.frame = self.mainBgLayer.frame.insetBy(dx: 4, dy: 0)
let font = NSFont(name: "RoadNumbers", size: self.mainBgLayer.frame.height*1.1) ?? NSFont.boldSystemFont(ofSize: 24)
let attributes: [NSAttributedString.Key: Any] = [
.kern: 3,
.font: font,
.foregroundColor: fgColor
]
let attributed = NSAttributedString(string: number.mainPart(), attributes: attributes)
self.numberLayer.string = attributed
let rbgSize = self.regionBgLayer.frame.size
self.regionLayer.frame = self.regionBgLayer.frame.inset(by: NSEdgeInsets(top: 2, left: 2, bottom: rbgSize.height*0.35, right: 2))
let regionFont = NSFont(name: "RoadNumbers", size: rbgSize.height*0.8) ?? NSFont.boldSystemFont(ofSize: 24)
let regionAttrs: [NSAttributedString.Key: Any] = [
.kern: 1,
.font: regionFont,
.foregroundColor: fgColor
]
let attributedRegion = NSAttributedString(string: number.region(), attributes: regionAttrs)
self.regionLayer.string = attributedRegion
self.countryLayer.foregroundColor = fgColor
self.countryLayer.frame = self.regionBgLayer.frame.inset(by: NSEdgeInsets(top: rbgSize.height*0.64,
left: rbgSize.width*0.08,
bottom: rbgSize.height*0.07,
right: rbgSize.width*0.42))
self.countryLayer.fontSize = self.countryLayer.frame.size.height
let top = (self.regionBgLayer.frame.origin.y + rbgSize.height*0.04).rounded(.toNearestOrAwayFromZero)
let left = (self.regionBgLayer.frame.origin.x + rbgSize.width*0.62).rounded(.toNearestOrAwayFromZero)
let w = (rbgSize.width*0.34).rounded(.toNearestOrAwayFromZero)
let h = (rbgSize.height*0.28).rounded(.toNearestOrAwayFromZero)
self.flagLayer.frame = CGRect(x: left, y: top, width: w, height: h)
self.flagLayer.setNeedsDisplay()
}
override var intrinsicContentSize: CGSize {
let height = fontSize/1.1 + 4
let width = height/PlateView.aspectRatio
return CGSize(width: width, height: height)
}
}
struct PlateNumberView: NSViewRepresentable {
var number: PlateNumber
var unrecognized: Bool
var outdated: Bool
var fontSize: CGFloat
func makeNSView(context: Context) -> PlateView {
let view = PlateView(frame: .zero)
view.fontSize = fontSize
return view
}
func updateNSView(_ nsView: PlateView, context: Context) {
nsView.number = number
nsView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
nsView.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
}
struct PlateNumberView_Previews: PreviewProvider {
static var previews: some View {
Group {
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: false, fontSize: 50)
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: true, outdated: false, fontSize: 50)
PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: true, fontSize: 50)
}
}
}
#endif

View File

@ -0,0 +1,40 @@
//
// PlateViewItem.swift
// AutoCat2SUI
//
// Created by Selim Mustafaev on 04.09.2022.
//
import SwiftUI
import AutoCatCore
struct PlateViewItem: View {
let vehicle: CDVehicle
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(vehicle.brand?.name?.original ?? "")
PlateNumberView(number: PlateNumber(vehicle.number ?? ""),
unrecognized: vehicle.unrecognized,
outdated: vehicle.outdated,
fontSize: 30)
}
.padding(.vertical, 2)
}
}
struct PlateViewItem_Previews: PreviewProvider {
static var previews: some View {
let test1 = Vehicle(number: "Н282СН61",
brandName: "KIA ОПТИМА",
addedDate: Date().timeIntervalSince1970,
updatedDate: Date().timeIntervalSince1970)
return PlateViewItem(vehicle: CDVehicle(vehicle: test1,
context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)))
}
}

View File

@ -28,7 +28,7 @@ public enum DataSource: CaseIterable, Identifiable {
public struct Filter: Hashable, Identifiable {
public let id = UUID().uuidString
public let id = UUID()
public let name: String
public let iconName: String
@ -48,6 +48,11 @@ public struct Filter: Hashable, Identifiable {
self.unrecognized = unrecognized
self.outdated = outdated
}
public func match(vehicle: CDVehicle) -> Bool {
return unrecognized == vehicle.unrecognized
&& outdated == vehicle.outdated
}
}
// MARK: - Presets

View File

@ -4,30 +4,39 @@ import DifferenceKit
public struct Vehicle: Decodable {
public let number: String
public let currentNumber: String?
public let brand: VBrand?
public let model: VModel?
public let addedDate: TimeInterval
public let updatedDate: TimeInterval
public let color: String?
public let year: Int64?
public let category: String?
public let vin1: String?
public let vin2: String?
public let sts: String?
public let pts: String?
public let isRightWheel: Bool?
public let isJapanese: Bool?
public let addedBy: String?
public let engine: VEngine?
public let photos: [VPhoto]?
public let ownershipPeriods: [VOwnershipPeriod]?
public let events: [VEvent]?
public let osagoContracts: [VOsago]?
public let ads: [VAd]?
public let notes: [VNote]?
public let debugInfo: DebugInfo?
public var number: String
public var currentNumber: String?
public var brand: VBrand?
public var model: VModel?
public var addedDate: TimeInterval
public var updatedDate: TimeInterval
public var color: String?
public var year: Int64?
public var category: String?
public var vin1: String?
public var vin2: String?
public var sts: String?
public var pts: String?
public var isRightWheel: Bool?
public var isJapanese: Bool?
public var addedBy: String?
public var engine: VEngine?
public var photos: [VPhoto]?
public var ownershipPeriods: [VOwnershipPeriod]?
public var events: [VEvent]?
public var osagoContracts: [VOsago]?
public var ads: [VAd]?
public var notes: [VNote]?
public var debugInfo: DebugInfo?
public init(number: String, brandName: String, addedDate: TimeInterval, updatedDate: TimeInterval) {
self.number = number
self.currentNumber = number
self.addedDate = addedDate
self.updatedDate = updatedDate
self.brand = VBrand(name: VName(original: brandName))
}
}
extension CDVehicle: Dated {
@ -54,7 +63,7 @@ extension CDVehicle: Differentiable {
extension CDVehicle {
convenience init(vehicle: Vehicle, context: NSManagedObjectContext) {
public convenience init(vehicle: Vehicle, context: NSManagedObjectContext) {
self.init(context: context)
self.number = vehicle.number

View File

@ -3,8 +3,8 @@ import CoreData
public struct VBrand: Decodable {
let logo: String?
let name: VName?
public var logo: String?
public var name: VName?
}
extension CDVBrand {

View File

@ -3,8 +3,8 @@ import CoreData
public struct VName: Decodable {
let normalized: String?
let original: String?
public var normalized: String?
public var original: String?
}
extension CDVName {

View File

@ -3,8 +3,8 @@ import Foundation
public struct Constants {
public static var baseUrl: String {
#if DEBUG
//return "http://127.0.0.1:3000/"
return "http://192.168.1.1:3000/"
return "http://127.0.0.1:3000/"
//return "http://192.168.1.1:3000/"
//return "https://vps.aliencat.pro:8443/"
#else
return "https://vps.aliencat.pro:8443/"