diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 210c6ad..8540678 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -178,7 +178,6 @@ 7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6CA02512244400F237B2 /* MapExt.swift */; }; 7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; }; 7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; }; - 7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; }; 7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */; }; 7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; }; 7AF6D2052677C03B0086EA64 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -445,7 +444,6 @@ 7ADF6CA02512244400F237B2 /* MapExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapExt.swift; sourceTree = ""; }; 7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = ""; }; 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = ""; }; - 7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = ""; }; 7AE8424D26109F78002F6B31 /* Exportable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exportable.swift; sourceTree = ""; }; 7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = ""; }; 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AutoCatCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -923,7 +921,6 @@ 7A813DC7250B5C6E00CC93B9 /* Location */ = { isa = PBXGroup; children = ( - 7AE26A3424F31B0700625033 /* EventsController.swift */, 7ADF6C94250D037700F237B2 /* ShowEventController.swift */, 7ADF6C9E251201D200F237B2 /* GlobalEventsController.swift */, ); @@ -1328,7 +1325,6 @@ 7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */, 7A761C0B267E8FF90005F28F /* Error.swift in Sources */, 7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */, - 7AE26A3524F31B0700625033 /* EventsController.swift in Sources */, 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */, 7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */, 7AB9FE262D08C2D7005DE374 /* EventsCoordinator.swift in Sources */, @@ -1693,7 +1689,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 144; + CURRENT_PROJECT_VERSION = 145; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1720,7 +1716,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 144; + CURRENT_PROJECT_VERSION = 145; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; diff --git a/AutoCat/Base.lproj/Main.storyboard b/AutoCat/Base.lproj/Main.storyboard index e829875..d236c0b 100644 --- a/AutoCat/Base.lproj/Main.storyboard +++ b/AutoCat/Base.lproj/Main.storyboard @@ -8,96 +8,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -780,7 +690,6 @@ - diff --git a/AutoCat/Controllers/Location/EventsController.swift b/AutoCat/Controllers/Location/EventsController.swift deleted file mode 100644 index 6f976f3..0000000 --- a/AutoCat/Controllers/Location/EventsController.swift +++ /dev/null @@ -1,418 +0,0 @@ -import UIKit -import MapKit -import RealmSwift -import PKHUD -import MobileCoreServices -import ExceptionCatcher -import AutoCatCore - -class EventPin: NSObject, MKAnnotation { - var coordinate: CLLocationCoordinate2D - var title: String? - var subtitle: String? - var id: String - - init(id: String, coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) { - self.coordinate = coordinate - self.title = title - self.subtitle = subtitle - self.id = id - } - - convenience init(event: VehicleEventDto) { - let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude) - let address = event.address ?? "\(event.latitude), \(event.longitude)" - let date = Date(timeIntervalSince1970: event.date) - let formatter = DateFormatter() - formatter.dateStyle = .medium - formatter.timeStyle = .short - let dateStr = formatter.string(from: date) - - if let number = event.number { - self.init(id: event.id, coordinate: coordinate, title: number, subtitle: dateStr) - } else { - self.init(id: event.id, coordinate: coordinate, title: dateStr, subtitle: address) - } - } -} - -enum EventsMode { - case map - case list -} - -class EventsController: UIViewController, UITableViewDataSource, UITableViewDelegate, MKMapViewDelegate { - - @IBOutlet weak var map: MKMapView! - @IBOutlet weak var tableView: UITableView! - - @Service var settingsService: SettingsServiceProtocol - - var modeButton: UIBarButtonItem! - var addButton: UIBarButtonItem! - var pasteButton: UIBarButtonItem! - var mode: EventsMode = .map - - public var vehicle: VehicleDto? { - didSet { - if self.isViewLoaded { - self.updateInterface() - } - } - } - - public var vehicleUpdated: ((VehicleDto) -> Void)? - - private var pins: [EventPin] = [] - - override func viewDidLoad() { - super.viewDidLoad() - - self.title = self.vehicle?.getNumber() ?? NSLocalizedString("Events", comment: "") - - #if targetEnvironment(macCatalyst) - - if #available(OSX 11.0, *) { - self.map.showsCompass = true - } - self.map.showsZoomControls = true - - #endif - - self.modeButton = UIBarButtonItem(image: UIImage(systemName: "list.bullet"), style: .plain, target: self, action: #selector(switchMode(_:))) - self.addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addEvent(_:))) - self.pasteButton = UIBarButtonItem(image: UIImage(systemName: "doc.on.clipboard"), style: .plain, target: self, action: #selector(pasteEvent(_:))) - self.setupBarButtonItems() - self.map.delegate = self - - self.updateInterface() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - self.setupBarButtonItems() - } - - func updateInterface() { - if let vehicle = self.vehicle { - self.pins = vehicle.events.map(EventPin.init(event:)) - } - - if !self.pins.isEmpty { - self.map.removeAnnotations(self.map.annotations) - self.map.addAnnotations(self.pins) - self.map.centerOnPins() - } else { - self.map.showsUserLocation = true - } - - self.tableView.reloadData() - } - - @objc func switchMode(_ sender: UIBarButtonItem) { - switch self.mode { - case .map: - self.mode = .list - self.tableView.reloadData() - self.tableView.isHidden = false - self.map.isHidden = true - self.modeButton.image = UIImage(systemName: "map") - case .list: - self.mode = .map - self.tableView.isHidden = true - self.map.isHidden = false - self.modeButton.image = UIImage(systemName: "list.bullet") - } - } - - // MARK: - UITableViewDataSource - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return self.vehicle?.events.count ?? 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as? EventCell else { - return UITableViewCell() - } - - if let event = self.vehicle?.events[indexPath.row] { - cell.configure(with: event) - } - - return cell - } - - // MARK: - UITableViewDelegate - - func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - guard let vehicle = self.vehicle else { - HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle")) - return nil - } - - let event = vehicle.events[indexPath.row] - - return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in - - let copy = UIAction(title: NSLocalizedString("Copy", comment: ""), - image: UIImage(systemName: "doc.on.doc"), - handler: { _ in self.copyEvent(event: event) }) - - let share = UIAction(title: NSLocalizedString("Share", comment: ""), - image: UIImage(systemName: "square.and.arrow.up"), - handler: { _ in self.shareEvent(event: event) }) - - let edit = UIAction(title: NSLocalizedString("Edit", comment: ""), - image: UIImage(systemName: "pencil"), - handler: { _ in self.editEvent(event: event) }) - - let delete = UIAction(title: NSLocalizedString("Delete", comment: ""), - image: UIImage(systemName: "trash"), - attributes: .destructive, - handler: { _ in self.deleteEvent(event: event) }) - - let openApple = UIAction(title: NSLocalizedString("Apple Maps", comment: ""), - image: UIImage(systemName: "map"), - handler: { _ in self.openInAppleMaps(event: event) }) - - let openYandex = UIAction(title: NSLocalizedString("Yandex Maps", comment: ""), - image: UIImage(systemName: "map"), - handler: { _ in self.openInYandexMaps(event: event) }) - - let openMenu: UIMenuElement - if let yandexUrl = URL(string: "yandexmaps://"), - UIApplication.shared.canOpenURL(yandexUrl) - { - openMenu = UIMenu(title: NSLocalizedString("Open in ...", comment: ""), - children: [openApple, openYandex]) - } else { - openApple.title = NSLocalizedString("Open in Apple Maps", comment: "") - openMenu = openApple - } - - return UIMenu(title: NSLocalizedString("Actions", comment: ""), children: [copy, share, edit, openMenu, delete]) - } - } - - func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - guard let vehicle = self.vehicle else { - HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle")) - return nil - } - - let event = vehicle.events[indexPath.row] - - let copy = UIContextualAction(style: .normal, title: NSLocalizedString("Copy", comment: "")) { action, view, completion in - self.copyEvent(event: event) - completion(true) - } - copy.image = UIImage(systemName: "doc.on.doc") - copy.backgroundColor = .systemBlue - - let delete = UIContextualAction(style: .destructive, title: NSLocalizedString("Delete", comment: "")) { action, view, completion in - self.deleteEvent(event: event, completion: completion) - } - delete.image = UIImage(systemName: "trash") - - let edit = UIContextualAction(style: .normal, title: NSLocalizedString("Edit", comment: "")) { action, view, completion in - self.editEvent(event: event) - completion(true) - } - edit.image = UIImage(systemName: "pencil") - edit.backgroundColor = .systemBlue - - let configuration = UISwipeActionsConfiguration(actions: [delete, edit, copy]) - configuration.performsFirstActionWithFullSwipe = false - return configuration - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let event = self.vehicle?.events[indexPath.row], - let pin = pins.first(where: { $0.id == event.id }) else { - return - } - - tableView.deselectRow(at: indexPath, animated: true) - switchMode(modeButton) - - map.setCenter(pin.coordinate, animated: true) - map.selectAnnotation(pin, animated: true) - } - - // MARK: - Event actions - - func deleteEvent(event: VehicleEventDto, completion: ((Bool) -> Void)? = nil) { - Task { - do { - HUD.show(.progress) - let vehicle = try await ApiService.shared.remove(event: event.id) - let result = self.update(vehicle: vehicle) - completion?(result) - } catch { - completion?(false) - HUD.show(error: error) - } - } - } - - func editEvent(event: VehicleEventDto) { - - if let navigationController { - let coordinator = LocationEditCoordinator(navController: navigationController) - Task { - if let event = await coordinator.start(event: event) { - do { - HUD.show(.progress) - let vehicle = try await ApiService.shared.edit(event: event) - self.update(vehicle: vehicle) - } catch { - HUD.show(error: error) - } - } - } - } - } - - func copyEvent(event: VehicleEventDto) { - var items: [String: Any] = [:] - - if let url = event.getMapLink() { - items[kUTTypeURL as String] = url - //UIPasteboard.general.setValue(url, forPasteboardType: kUTTypeURL as String) - } - - if let data = try? JSONEncoder().encode(event) { - items["pro.aliencat.vehicle.event"] = data - //UIPasteboard.general.setData(data, forPasteboardType: "pro.aliencat.vehicle.event") - } - - if !items.isEmpty { - UIPasteboard.general.items = [items] - } - - self.setupBarButtonItems() - } - - func shareEvent(event: VehicleEventDto) { - guard let url = event.getMapLink() else { - return - } - - let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil) - self.present(controller, animated: true) - } - - func openInAppleMaps(event: VehicleEventDto) { - let coordinates = CLLocationCoordinate2D(latitude: event.latitude, - longitude: event.longitude) - let placemark = MKPlacemark(coordinate: coordinates) - let mapItem = MKMapItem(placemark: placemark) - mapItem.openInMaps() - } - - func openInYandexMaps(event: VehicleEventDto) { - guard let url = URL(string: "yandexmaps://maps.yandex.ru/?pt=\(event.longitude),\(event.latitude)&z=12") else { - return - } - - UIApplication.shared.open(url) - } - - @objc func addEvent(_ sender: UIBarButtonItem) { - guard let vehicle = self.vehicle else { - HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle")) - return - } - - if let navigationController { - let coordinator = LocationEditCoordinator(navController: navigationController) - - Task { - let newEvent = VehicleEventDto(lat: 0, lon: 0, addedBy: settingsService.user.email) - if let event = await coordinator.start(event: newEvent) { - do { - HUD.show(.progress) - let vehicle = try await ApiService.shared.add(event: event, to: vehicle.getNumber()) - self.update(vehicle: vehicle) - } catch { - HUD.show(error: error) - } - } - } - } - } - - @objc func pasteEvent(_ sender: UIBarButtonItem) { - guard let vehicle = self.vehicle else { - HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle")) - return - } - guard let data = UIPasteboard.general.data(forPasteboardType: "pro.aliencat.vehicle.event") else { return } - - do { - let event = try JSONDecoder().decode(VehicleEventDto.self, from: data) - let formatter = DateFormatter() - formatter.dateStyle = .medium - formatter.timeStyle = .medium - let msg = formatter.string(from: Date(timeIntervalSince1970: event.date)) + "\n" + event.getLocationString() - - let alert = UIAlertController(title: NSLocalizedString("Paste event", comment: "from clipboard"), message: msg, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("Paste", comment: "from clipboard"), style: .default, handler: { action in - Task { - do { - HUD.show(.progress) - var newEvent = event - newEvent.id = UUID().uuidString - let vehicle = try await ApiService.shared.add(event: newEvent, to: vehicle.getNumber()) - self.update(vehicle: vehicle) - } catch { - HUD.show(error: error) - } - } - })) - alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil)) - self.present(alert, animated: true) - } catch { - print(error) - } - } - - func setupBarButtonItems() { - let pasteVisible = UIPasteboard.general.data(forPasteboardType: "pro.aliencat.vehicle.event") != nil - self.navigationItem.rightBarButtonItems = pasteVisible ? [self.modeButton, self.addButton, self.pasteButton] : [self.modeButton, self.addButton] - } - - @discardableResult - func update(vehicle: VehicleDto) -> Bool { - do { - let realm = try Realm() - if realm.object(ofType: Vehicle.self, forPrimaryKey: vehicle.getNumber()) != nil { - try ExceptionCatcher.catch { - try realm.write { - realm.add(Vehicle(dto: vehicle), update: .all) - } - } - } - - self.vehicle?.events = vehicle.events.sorted { $0.date > $1.date } - self.vehicleUpdated?(vehicle) - - HUD.hide() - return true - } catch { - HUD.hide() - self.show(error: error) - print(error) - return false - } - } - - // MARK: - MKMapViewDelegate - - public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) { - let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000) - self.map.setRegion(region, animated: true) - self.map.showsUserLocation = false - } -} diff --git a/AutoCat/Controllers/Location/GlobalEventsController.swift b/AutoCat/Controllers/Location/GlobalEventsController.swift index 5a3a249..0628720 100644 --- a/AutoCat/Controllers/Location/GlobalEventsController.swift +++ b/AutoCat/Controllers/Location/GlobalEventsController.swift @@ -3,6 +3,36 @@ import MapKit import PKHUD import AutoCatCore +class EventPin: NSObject, MKAnnotation { + var coordinate: CLLocationCoordinate2D + var title: String? + var subtitle: String? + var id: String + + init(id: String, coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) { + self.coordinate = coordinate + self.title = title + self.subtitle = subtitle + self.id = id + } + + convenience init(event: VehicleEventDto) { + let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude) + let address = event.address ?? "\(event.latitude), \(event.longitude)" + let date = Date(timeIntervalSince1970: event.date) + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + let dateStr = formatter.string(from: date) + + if let number = event.number { + self.init(id: event.id, coordinate: coordinate, title: number, subtitle: dateStr) + } else { + self.init(id: event.id, coordinate: coordinate, title: dateStr, subtitle: address) + } + } +} + class GlobalEventsController: UIViewController { @IBOutlet weak var map: MKMapView! diff --git a/AutoCat/Screens/EventsScreen/EventsScreen.swift b/AutoCat/Screens/EventsScreen/EventsScreen.swift index 6b23bd4..edaee12 100644 --- a/AutoCat/Screens/EventsScreen/EventsScreen.swift +++ b/AutoCat/Screens/EventsScreen/EventsScreen.swift @@ -138,9 +138,13 @@ struct EventsScreen: View { } } +#if DEBUG + #Preview { EventsScreen(viewModel: .init(apiService: MockApiServiceProtocol(), storageService: MockStorageServiceProtocol(), settingsService: MockSettingsServiceProtocol(), vehicle: .preview)) } + +#endif diff --git a/AutoCat/Screens/EventsScreen/EventsViewModel.swift b/AutoCat/Screens/EventsScreen/EventsViewModel.swift index e564623..a12d335 100644 --- a/AutoCat/Screens/EventsScreen/EventsViewModel.swift +++ b/AutoCat/Screens/EventsScreen/EventsViewModel.swift @@ -89,8 +89,10 @@ class EventsViewModel: ACHudContainer { func addNewEvent() async { - let newEvent = VehicleEventDto(lat: 0, lon: 0, addedBy: settingsService.user.email) - await addEvent(newEvent) + let emptyEvent = VehicleEventDto(lat: 0, lon: 0, addedBy: settingsService.user.email) + if let newEvent = await coordinator?.editEvent(event: emptyEvent) { + await addEvent(newEvent) + } } func deleteEvent(_ event: EventModel) async { diff --git a/AutoCat/Screens/FiltersScreen/FiltersScreen.swift b/AutoCat/Screens/FiltersScreen/FiltersScreen.swift index 7140fcb..337b00e 100644 --- a/AutoCat/Screens/FiltersScreen/FiltersScreen.swift +++ b/AutoCat/Screens/FiltersScreen/FiltersScreen.swift @@ -119,9 +119,13 @@ struct FiltersScreen: View { } } +#if DEBUG + #Preview { FiltersScreen(viewModel: .init( apiService: MockApiServiceProtocol(), filter: Filter() )) } + +#endif diff --git a/AutoCat/Screens/NotesScreen/NotesScreen.swift b/AutoCat/Screens/NotesScreen/NotesScreen.swift index 05187fe..c73b652 100644 --- a/AutoCat/Screens/NotesScreen/NotesScreen.swift +++ b/AutoCat/Screens/NotesScreen/NotesScreen.swift @@ -86,6 +86,8 @@ struct NotesScreen: View { } } +#if DEBUG + #Preview { var vehicle = VehicleDto() @@ -104,3 +106,5 @@ struct NotesScreen: View { return NotesScreen(viewModel: vm) } + +#endif diff --git a/AutoCat/Screens/ReportScreen/ReportScreen.swift b/AutoCat/Screens/ReportScreen/ReportScreen.swift index 07064ab..53aa047 100644 --- a/AutoCat/Screens/ReportScreen/ReportScreen.swift +++ b/AutoCat/Screens/ReportScreen/ReportScreen.swift @@ -125,6 +125,8 @@ struct ReportScreen: View { } } +#if DEBUG + #Preview { ReportScreen(viewModel: .init( apiService: MockApiServiceProtocol(), @@ -134,3 +136,5 @@ struct ReportScreen: View { isPersistent: false )) } + +#endif diff --git a/AutoCat/Screens/SettingsScreen/SettingsScreen.swift b/AutoCat/Screens/SettingsScreen/SettingsScreen.swift index 009b670..44a6f95 100644 --- a/AutoCat/Screens/SettingsScreen/SettingsScreen.swift +++ b/AutoCat/Screens/SettingsScreen/SettingsScreen.swift @@ -87,6 +87,10 @@ struct SettingsScreen: View { } } +#if DEBUG + #Preview { SettingsScreen(viewModel: .init(settingsService: MockSettingsServiceProtocol())) } + +#endif