From c59c102f6206cd668f1d5674c7c3d27bde4c1d46 Mon Sep 17 00:00:00 2001 From: Emin Date: Wed, 25 Sep 2024 23:11:53 +0500 Subject: [PATCH] ios: map tasks finished (create route, download map, remove unnecessary UI), ios finished, global: change base_url, other little changes --- 3party/expat | 2 +- 3party/icu/icu | 2 +- android/app/build.gradle | 2 +- .../src/main/java/app/tourism/Constants.kt | 2 +- .../app/src/main/res/values-ru/strings.xml | 2 +- android/app/src/main/res/values/strings.xml | 2 +- iphone/Maps/Classes/BackButtonWithText.m | 51 +++++++++++ .../BackButtonWithText/BackButtonWithText.h | 11 +++ .../BackButtonWithText/BackButtonWithText.m | 51 +++++++++++ iphone/Maps/Classes/MapViewController.h | 2 + iphone/Maps/Classes/MapViewController.mm | 87 ++++++++++++++++++- iphone/Maps/Classes/MapsAppDelegate.mm | 35 +++++++- .../Classes/Widgets/MWMMapDownloadDialog.mm | 20 +++-- .../en-GB.lproj/Localizable.strings | 2 +- .../en.lproj/Localizable.strings | 2 +- .../ru.lproj/Localizable.strings | 2 +- iphone/Maps/Maps.xcodeproj/project.pbxproj | 14 +++ iphone/Maps/Tourism/Constants.swift | 4 +- .../Network/Services/ProfileService.swift | 2 +- .../Tourism/Data/Prefs/UserPreferences.swift | 34 +++++++- .../Repositories/PlacesRepositoryImpl.swift | 8 +- .../Tourism/Domain/Models/PlaceLocation.swift | 15 +++- .../Categories/CategoriesViewController.swift | 38 ++++++-- .../Favorites/FavoritesViewController.swift | 17 +++- .../New Group/HomeViewController.swift | 50 ++++++++--- .../PlaceDetails/PlaceViewController.swift | 35 ++++++-- .../Screens/Search/SearchViewController.swift | 18 +++- .../Presentation/Home/TabBarController.swift | 34 ++++++-- .../Presentation/Home/TourismMain.storyboard | 4 +- .../Presentation/Utils/NavigationUtils.swift | 12 ++- .../Components/ActionBarViewController.swift | 11 --- 31 files changed, 491 insertions(+), 80 deletions(-) create mode 100644 iphone/Maps/Classes/BackButtonWithText.m create mode 100644 iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.h create mode 100644 iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.m diff --git a/3party/expat b/3party/expat index 9cbdb916de..74d91febb0 160000 --- a/3party/expat +++ b/3party/expat @@ -1 +1 @@ -Subproject commit 9cbdb916de2a7bd1aa649e55efc38d2426680359 +Subproject commit 74d91febb0995b7c6706dfd4eed2d39fb1694421 diff --git a/3party/icu/icu b/3party/icu/icu index 680f521746..6af11aa609 160000 --- a/3party/icu/icu +++ b/3party/icu/icu @@ -1 +1 @@ -Subproject commit 680f521746a3bd6a86f25f25ee50a62d88b489cf +Subproject commit 6af11aa609f3fdf735cab5fdc051cd840960186b diff --git a/android/app/build.gradle b/android/app/build.gradle index d0dae35516..9d982b3096 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -111,7 +111,7 @@ android { defaultConfig { // Default package name is taken from the manifest and should be app.organicmaps def ver = getVersion() - versionCode = 2 + versionCode = 3 versionName = "1.0.0" // println('Version: ' + versionName) // println('VersionCode: ' + versionCode) diff --git a/android/app/src/main/java/app/tourism/Constants.kt b/android/app/src/main/java/app/tourism/Constants.kt index fb83fb3615..1aa012fc03 100644 --- a/android/app/src/main/java/app/tourism/Constants.kt +++ b/android/app/src/main/java/app/tourism/Constants.kt @@ -17,7 +17,7 @@ import app.organicmaps.R import app.tourism.ui.theme.getBorderColor const val TAG = "GLOBAL_TAG" -const val BASE_URL = "https://product.rebus.tj" +const val BASE_URL = "https://tourismmap.tj" object Constants { // UI diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml index 9859992657..e35796fc6b 100644 --- a/android/app/src/main/res/values-ru/strings.xml +++ b/android/app/src/main/res/values-ru/strings.xml @@ -2161,7 +2161,7 @@ Коллекции Комитет по развитию туризма при Правительстве Республики Таджикистан Упс, что-то пошло не так - Пожалуйста, подождите, идет загрузка карты Таджикистана. Оставайтесь в приложении + Пожалуйста, подождите, идет загрузка карты Таджикистана. Добро пожаловать в Таджикистан Войти Регистрация diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index bb248b7321..777d5c4d6a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -2203,7 +2203,7 @@ //todo Committee for Tourism Development under the Government of the Republic of Tajikistan Error - Please wait, the map of Tajikistan is loading, stay in the app + Please wait, the map of Tajikistan is loading Welcome to Tajikistan Log in Registration diff --git a/iphone/Maps/Classes/BackButtonWithText.m b/iphone/Maps/Classes/BackButtonWithText.m new file mode 100644 index 0000000000..ba1218fb72 --- /dev/null +++ b/iphone/Maps/Classes/BackButtonWithText.m @@ -0,0 +1,51 @@ +#import + +@interface BackButtonWithText : UIView + +@property (nonatomic, strong) UIButton *backButton; +@property (nonatomic, strong) UILabel *backLabel; + +- (instancetype)initWithFrame:(CGRect)frame; +- (void)setBackButtonAction:(SEL)action target:(id)target; + +@end + +@implementation BackButtonWithText + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setupBackButton]; + [self setupBackLabel]; + } + return self; +} + +- (void)setupBackButton { + self.backButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.backButton setImage:[UIImage imageNamed:@"back_arrow"] forState:UIControlStateNormal]; + self.backButton.frame = CGRectMake(0, 0, 44, 44); + [self addSubview:self.backButton]; +} + +- (void)setupBackLabel { + self.backLabel = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 100, 44)]; + self.backLabel.text = @"Back"; + self.backLabel.textColor = [UIColor blackColor]; + self.backLabel.userInteractionEnabled = YES; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelTapped:)]; + [self.backLabel addGestureRecognizer:tapGesture]; + + [self addSubview:self.backLabel]; +} + +- (void)setBackButtonAction:(SEL)action target:(id)target { + [self.backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; +} + +- (void)labelTapped:(UITapGestureRecognizer *)gesture { + [self.backButton sendActionsForControlEvents:UIControlEventTouchUpInside]; +} + +@end diff --git a/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.h b/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.h new file mode 100644 index 0000000000..a8616f5ee7 --- /dev/null +++ b/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.h @@ -0,0 +1,11 @@ +#import + +@interface BackButtonWithText : UIView + +@property (nonatomic, strong) UIButton *backButton; +@property (nonatomic, strong) UILabel *backLabel; + +- (instancetype)initWithFrame:(CGRect)frame; +- (void)setBackButtonAction:(SEL)action target:(id)target; + +@end diff --git a/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.m b/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.m new file mode 100644 index 0000000000..ba1218fb72 --- /dev/null +++ b/iphone/Maps/Classes/BackButtonWithText/BackButtonWithText.m @@ -0,0 +1,51 @@ +#import + +@interface BackButtonWithText : UIView + +@property (nonatomic, strong) UIButton *backButton; +@property (nonatomic, strong) UILabel *backLabel; + +- (instancetype)initWithFrame:(CGRect)frame; +- (void)setBackButtonAction:(SEL)action target:(id)target; + +@end + +@implementation BackButtonWithText + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setupBackButton]; + [self setupBackLabel]; + } + return self; +} + +- (void)setupBackButton { + self.backButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.backButton setImage:[UIImage imageNamed:@"back_arrow"] forState:UIControlStateNormal]; + self.backButton.frame = CGRectMake(0, 0, 44, 44); + [self addSubview:self.backButton]; +} + +- (void)setupBackLabel { + self.backLabel = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 100, 44)]; + self.backLabel.text = @"Back"; + self.backLabel.textColor = [UIColor blackColor]; + self.backLabel.userInteractionEnabled = YES; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelTapped:)]; + [self.backLabel addGestureRecognizer:tapGesture]; + + [self addSubview:self.backLabel]; +} + +- (void)setBackButtonAction:(SEL)action target:(id)target { + [self.backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; +} + +- (void)labelTapped:(UITapGestureRecognizer *)gesture { + [self.backButton sendActionsForControlEvents:UIControlEventTouchUpInside]; +} + +@end diff --git a/iphone/Maps/Classes/MapViewController.h b/iphone/Maps/Classes/MapViewController.h index 3657a6e340..931c4e62dc 100644 --- a/iphone/Maps/Classes/MapViewController.h +++ b/iphone/Maps/Classes/MapViewController.h @@ -34,10 +34,12 @@ - (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration; + (void)setViewport:(double)lat lon:(double)lon zoomLevel:(int)zoomlevel; ++ (void)setViewportToDushanbe; - (void)initialize; - (void)enableCarPlayRepresentation; - (void)disableCarPlayRepresentation; +- (void)backToTourismMain; - (void)dismissPlacePage; diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 5743e89b33..92f393cfec 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -14,6 +14,9 @@ #import "MWMPlacePageProtocol.h" #import "MapsAppDelegate.h" #import "SwiftBridge.h" +#import "MWMRoutePoint+CPP.h" + +#import "BackButtonWithText.h" #import #import @@ -367,6 +370,10 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; // Otherwise PP container view is nil, or there is no animation/selection of the point. if (DeepLinkHandler.shared.isLaunchedByDeeplink) (void)[DeepLinkHandler.shared handleDeepLinkAndReset]; + + [self showButtonToTourismMain]; + + [self createRoute]; } - (void)viewDidLayoutSubviews { @@ -536,7 +543,20 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; #pragma mark - MWMFrameworkDrapeObserver - (void)processViewportCountryEvent:(CountryId const &)countryId { - [self.downloadDialog processViewportCountryEvent:countryId]; + if (countryId == "Tajikistan") { + [self.downloadDialog processViewportCountryEvent:countryId]; + } else { + auto &f = GetFramework(); + + ms::LatLon viewportCenterLocation = mercator::ToLatLon(f.GetViewportCenter()); + BOOL isInBounds = isLocationInBounds(viewportCenterLocation, + ms::LatLon(41.196740, 66.949922), + ms::LatLon(36.483415, 75.400353)); + if (!isInBounds) { + [MapViewController setViewportToDushanbe]; + [[MWMToast toastWithText:L(@"plz_dont_go_out_of_tjk")] show]; + } + } } #pragma mark - Authorization @@ -740,4 +760,69 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; } } +#pragma mark - Functions for Tourism purposes +- (void) createRoute { + TourismUserPreferences *prefs = [TourismUserPreferences shared]; + if (!prefs.isLocationEmpty) { + PlaceLocation *location = [prefs getLocation]; + + m2::PointD pointD = mercator::FromLatLon(ms::LatLon(location.lat, location.lon)); + auto point = [[MWMRoutePoint alloc] initWithPoint:pointD + title:location.name + subtitle:@"" + type:MWMRoutePointType::MWMRoutePointTypeFinish + intermediateIndex:0]; + + [MWMRouter buildToPoint:point bestRouter:NO]; + + // we clear location so next time, when we get back to map it doesn't create route again + [prefs clearLocation]; + } +} + +BOOL isLocationInBounds(ms::LatLon location, ms::LatLon topLeft, ms::LatLon bottomRight) { + return (location.m_lat <= topLeft.m_lat && + location.m_lat >= bottomRight.m_lat && + location.m_lon >= topLeft.m_lon && + location.m_lon <= bottomRight.m_lon); +} + ++ (void)setViewportToDushanbe { + [self setViewport: 38.5598 lon: 68.7870 zoomLevel: 10]; +} + +- (void)showButtonToTourismMain { + UIButton *homeButton = [UIButton buttonWithType:UIButtonTypeSystem]; + + [homeButton setTitle:NSLocalizedString(@"home", nil) forState:UIControlStateNormal]; + [homeButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + homeButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5]; + homeButton.layer.cornerRadius = 10.0; + homeButton.clipsToBounds = YES; + + homeButton.translatesAutoresizingMaskIntoConstraints = NO; + + [homeButton addTarget:self action:@selector(backToTourismMain) forControlEvents:UIControlEventTouchUpInside]; + + [self.controlsView addSubview:homeButton]; + + UIImage *homeIcon = [UIImage systemImageNamed:@"house.fill"]; + homeButton.tintColor = [UIColor blackColor]; + [homeButton setImage:homeIcon forState:UIControlStateNormal]; + + homeButton.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight; + homeButton.imageEdgeInsets = UIEdgeInsetsMake(0, -4, 0, 0); + homeButton.contentEdgeInsets = UIEdgeInsetsMake(12, 8, 12, 8); + + [NSLayoutConstraint activateConstraints:@[ + [homeButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], + [homeButton.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:120], + [homeButton.heightAnchor constraintEqualToConstant:50] + ]]; + +} + +- (void)backToTourismMain { + [[MapViewController sharedController]performSegueWithIdentifier:@"Map2TourismMain" sender:nil]; +} @end diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index ec3c456a86..5f4b42d0c8 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -209,9 +209,14 @@ using namespace osm_auth_ios; - (void)applicationDidBecomeActive:(UIApplication *)application { LOG(LINFO, ("applicationDidBecomeActive - begin")); - [[MapViewController sharedController]performSegueWithIdentifier:@"Map2TourismMain" sender:nil]; - auto & f = GetFramework(); + + // MARK: Our default app entry point is TourismMain that's why we go there + [self goToTourismMainIfTajikistanIsLoaded]; + + [self moveToDushanbeIfNotInTjk]; + + f.EnterForeground(); [self.mapViewController onGetFocus:YES]; f.SetRenderingEnabled(); @@ -229,6 +234,32 @@ using namespace osm_auth_ios; LOG(LINFO, ("applicationDidBecomeActive - end")); } + +// MARK: Functions for Tourism purposes + +- (void) goToTourismMainIfTajikistanIsLoaded { + auto & f = GetFramework(); + if(f.IsCountryLoadedByName("Tajikistan")) { + [[MapViewController sharedController]performSegueWithIdentifier:@"Map2TourismMain" sender:nil]; + } +} + +- (void) moveToDushanbeIfNotInTjk { + auto & f = GetFramework(); + ms::LatLon viewportCenterLocation = mercator::ToLatLon(f.GetViewportCenter()); + BOOL isInBounds = isLocationInBounds1(viewportCenterLocation, + ms::LatLon(41.196740, 66.949922), + ms::LatLon(36.483415, 75.400353)); + if (!isInBounds) [MapViewController setViewportToDushanbe]; +} + +BOOL isLocationInBounds1(ms::LatLon location, ms::LatLon topLeft, ms::LatLon bottomRight) { + return (location.m_lat <= topLeft.m_lat && + location.m_lat >= bottomRight.m_lat && + location.m_lon >= topLeft.m_lon && + location.m_lon <= bottomRight.m_lon); +} + // TODO: Drape enabling and iCloud sync are skipped during the test run due to the app crashing in teardown. This is a temporary solution. Drape should be properly disabled instead of merely skipping the enabling process. + (BOOL)isTestsEnvironment { NSProcessInfo * processInfo = [NSProcessInfo processInfo]; diff --git a/iphone/Maps/Classes/Widgets/MWMMapDownloadDialog.mm b/iphone/Maps/Classes/Widgets/MWMMapDownloadDialog.mm index 7900f6ce9c..26fcd65f15 100644 --- a/iphone/Maps/Classes/Widgets/MWMMapDownloadDialog.mm +++ b/iphone/Maps/Classes/Widgets/MWMMapDownloadDialog.mm @@ -80,7 +80,7 @@ using namespace storage; self.parentNode.text = @(nodeAttrs.m_topmostParentInfo[0].m_localName.c_str()); self.parentNode.textColor = [UIColor blackSecondaryText]; } - self.node.text = @(nodeAttrs.m_nodeLocalName.c_str()); + self.node.text = L(@"wait_tjk_map_downloading"); self.node.textColor = [UIColor blackPrimaryText]; self.nodeSize.hidden = NO; self.nodeSize.textColor = [UIColor blackSecondaryText]; @@ -144,6 +144,10 @@ using namespace storage; // Center dialog in the parent view. [self.centerXAnchor constraintEqualToAnchor:controller.view.centerXAnchor].active = YES; [self.centerYAnchor constraintEqualToAnchor:controller.view.centerYAnchor].active = YES; + [self.topAnchor constraintEqualToAnchor:controller.view.topAnchor].active = YES; + [self.bottomAnchor constraintEqualToAnchor:controller.view.bottomAnchor].active = YES; + [self.leftAnchor constraintEqualToAnchor:controller.view.leftAnchor].active = YES; + [self.rightAnchor constraintEqualToAnchor:controller.view.rightAnchor].active = YES; } @@ -233,8 +237,13 @@ using namespace storage; - (void)processCountry:(NSString *)countryId downloadedBytes:(uint64_t)downloadedBytes totalBytes:(uint64_t)totalBytes { - if (self.superview && m_countryId == countryId.UTF8String) + if (self.superview && m_countryId == countryId.UTF8String) { [self showDownloading:(CGFloat)downloadedBytes / totalBytes]; + } + + if(downloadedBytes == totalBytes) { + [[MapViewController sharedController]performSegueWithIdentifier:@"Map2TourismMain" sender:nil]; + } } #pragma mark - MWMCircularProgressDelegate @@ -244,9 +253,10 @@ using namespace storage; [self showInQueue]; [[MWMStorage sharedStorage] retryDownloadNode:@(m_countryId.c_str())]; } else { - if (m_autoDownloadCountryId == m_countryId) - self.isAutoDownloadCancelled = YES; - [[MWMStorage sharedStorage] cancelDownloadNode:@(m_countryId.c_str())]; + // we're forcing the user to download Tajikistan map, so we remove cancel button +// if (m_autoDownloadCountryId == m_countryId) +// self.isAutoDownloadCancelled = YES; +// [[MWMStorage sharedStorage] cancelDownloadNode:@(m_countryId.c_str())]; } } diff --git a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings index 30c1acda3d..07ef0aa517 100644 --- a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings @@ -3951,7 +3951,7 @@ "cache_error" = "Cache error, try to clean cache"; -"wait_tjk_map_downloading" = "Please wait, the map of Tajikistan is loading, stay in the app"; +"wait_tjk_map_downloading" = "Please wait, the map of Tajikistan is loading"; "welcome_to_tjk" = "Welcome to Tajikistan"; diff --git a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings index d2416cc9c0..eb853175d9 100644 --- a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings @@ -3951,7 +3951,7 @@ "cache_error" = "Cache error, try to clean cache"; -"wait_tjk_map_downloading" = "Please wait, the map of Tajikistan is loading, stay in the app"; +"wait_tjk_map_downloading" = "Please wait, the map of Tajikistan is loading"; "welcome_to_tjk" = "Welcome to Tajikistan"; diff --git a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings index 8c6e167841..1db5a027ca 100644 --- a/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/ru.lproj/Localizable.strings @@ -3951,7 +3951,7 @@ "error" = "Ошибка"; -"wait_tjk_map_downloading" = "Пожалуйста, подождите, идет загрузка карты Таджикистана. Оставайтесь в приложении"; +"wait_tjk_map_downloading" = "Пожалуйста, подождите, идет загрузка карты Таджикистана"; "welcome_to_tjk" = "Добро пожаловать в Таджикистан"; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index a36afc49a2..312f197414 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -583,6 +583,7 @@ CDCA27842245090900167D87 /* ListenerContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCA27832245090900167D87 /* ListenerContainer.swift */; }; CDCA278622451F5000167D87 /* RouteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCA278522451F5000167D87 /* RouteInfo.swift */; }; CDCA278E2248F34C00167D87 /* MWMRoutingManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDCA278B2248F34C00167D87 /* MWMRoutingManager.mm */; }; + CE2D27F82CA2C49F00094565 /* BackButtonWithText.m in Sources */ = {isa = PBXBuildFile; fileRef = CE2D27F72CA2C49F00094565 /* BackButtonWithText.m */; }; CE64501B2C93F5840075A59B /* PlacePersistenceControllerTesterBro.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE64501A2C93F5840075A59B /* PlacePersistenceControllerTesterBro.swift */; }; CE64501D2C93F8350075A59B /* ReviewsPersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE64501C2C93F8350075A59B /* ReviewsPersistenceController.swift */; }; CE6450202C9402EC0075A59B /* ReviewsPersistenceControllerTesterBro.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE64501F2C9402EC0075A59B /* ReviewsPersistenceControllerTesterBro.swift */; }; @@ -1635,6 +1636,8 @@ CDCA278C2248F34C00167D87 /* MWMRouterResultCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMRouterResultCode.h; sourceTree = ""; }; CDCA278F2248F3B800167D87 /* MWMLocationModeListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMLocationModeListener.h; sourceTree = ""; }; CDE0F3AD225B8D45008BA5C3 /* MWMSpeedCameraManagerMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMSpeedCameraManagerMode.h; sourceTree = ""; }; + CE2D27F72CA2C49F00094565 /* BackButtonWithText.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BackButtonWithText.m; sourceTree = ""; }; + CE2D27FB2CA2C64700094565 /* BackButtonWithText.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BackButtonWithText.h; sourceTree = ""; }; CE64501A2C93F5840075A59B /* PlacePersistenceControllerTesterBro.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePersistenceControllerTesterBro.swift; sourceTree = ""; }; CE64501C2C93F8350075A59B /* ReviewsPersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewsPersistenceController.swift; sourceTree = ""; }; CE64501F2C9402EC0075A59B /* ReviewsPersistenceControllerTesterBro.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewsPersistenceControllerTesterBro.swift; sourceTree = ""; }; @@ -2093,6 +2096,7 @@ F607C18B1C047FCA00B53A87 /* Segue */, 340837101B7243B500B5C185 /* Share */, F6588E291B15C25C00EE1E58 /* TextView */, + CE2D27FA2CA2C61500094565 /* BackButtonWithText */, FA8E808825F412E2002A1434 /* FirstSession.mm */, FA8E808A25F41337002A1434 /* FirstSession.h */, ); @@ -3865,6 +3869,15 @@ path = Location; sourceTree = ""; }; + CE2D27FA2CA2C61500094565 /* BackButtonWithText */ = { + isa = PBXGroup; + children = ( + CE2D27F72CA2C49F00094565 /* BackButtonWithText.m */, + CE2D27FB2CA2C64700094565 /* BackButtonWithText.h */, + ); + path = BackButtonWithText; + sourceTree = ""; + }; CE6450192C93F56E0075A59B /* Testers */ = { isa = PBXGroup; children = ( @@ -5253,6 +5266,7 @@ 99514BB823E82B450085D3A7 /* ElevationProfilePresenter.swift in Sources */, 34C9BD031C6DB693000DC38D /* MWMTableViewController.m in Sources */, 52E95F0D2C6C797B00A3FE2E /* ProfileViewController.swift in Sources */, + CE2D27F82CA2C49F00094565 /* BackButtonWithText.m in Sources */, F6E2FD8C1E097BA00083EBEC /* MWMNoMapsView.m in Sources */, 529A5F312C86DF61004FE4A1 /* Review Models.swift in Sources */, 34D3B0361E389D05004100F9 /* MWMEditorSelectTableViewCell.m in Sources */, diff --git a/iphone/Maps/Tourism/Constants.swift b/iphone/Maps/Tourism/Constants.swift index 2d1bac7b81..8a11522b89 100644 --- a/iphone/Maps/Tourism/Constants.swift +++ b/iphone/Maps/Tourism/Constants.swift @@ -79,5 +79,5 @@ struct Constants { ) } -let BASE_URL_WITHOUT_API = "https://product.rebus.tj/" -let BASE_URL = "https://product.rebus.tj/api/" +let BASE_URL_WITHOUT_API = "https://tourismmap.tj/" +let BASE_URL = "https://tourismmap.tj/api/" diff --git a/iphone/Maps/Tourism/Data/Network/Services/ProfileService.swift b/iphone/Maps/Tourism/Data/Network/Services/ProfileService.swift index 8586cae5fb..74e03f16a2 100644 --- a/iphone/Maps/Tourism/Data/Network/Services/ProfileService.swift +++ b/iphone/Maps/Tourism/Data/Network/Services/ProfileService.swift @@ -91,7 +91,7 @@ class ProfileServiceImpl: ProfileService { body += Data("--\(boundary)--\r\n".utf8) - var request = URLRequest(url: URL(string: "https://product.rebus.tj/api/profile")!, timeoutInterval: Double.infinity) + var request = URLRequest(url: URL(string: APIEndpoints.updateProfileUrl)!, timeoutInterval: Double.infinity) request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") if let token = userPreferences.getToken() { diff --git a/iphone/Maps/Tourism/Data/Prefs/UserPreferences.swift b/iphone/Maps/Tourism/Data/Prefs/UserPreferences.swift index 83b5a19308..5b1351f469 100644 --- a/iphone/Maps/Tourism/Data/Prefs/UserPreferences.swift +++ b/iphone/Maps/Tourism/Data/Prefs/UserPreferences.swift @@ -1,9 +1,10 @@ import Foundation -class UserPreferences { - static let shared = UserPreferences() +@objc(TourismUserPreferences) +class UserPreferences: NSObject { + @objc static let shared = UserPreferences() - private init() {} + private override init() {} private let userDefaults = UserDefaults.standard @@ -60,4 +61,31 @@ class UserPreferences { func setUserId(value: String?) { userDefaults.set(value, forKey: "user_id") } + + @objc func getLocation() -> PlaceLocation? { + let name = userDefaults.string(forKey: "name") + let lat = userDefaults.double(forKey: "lat") + let lon = userDefaults.double(forKey: "lon") + return PlaceLocation(name: name ?? "", lat: lat, lon: lon) + } + + @objc func setLocation(value: PlaceLocation) { + userDefaults.set(value.name, forKey: "name") + userDefaults.set(value.lat, forKey: "lat") + userDefaults.set(value.lon, forKey: "lon") + } + + @objc func clearLocation() { + userDefaults.removeObject(forKey: "name") + userDefaults.removeObject(forKey: "lat") + userDefaults.removeObject(forKey: "lon") + } + + @objc func isLocationEmpty() -> Bool { + let location = getLocation() + if let location { + return location.lat == 0.0 && location.lon == 0.0 + } + return true + } } diff --git a/iphone/Maps/Tourism/Data/Repositories/PlacesRepositoryImpl.swift b/iphone/Maps/Tourism/Data/Repositories/PlacesRepositoryImpl.swift index c115b7733c..45eac71cee 100644 --- a/iphone/Maps/Tourism/Data/Repositories/PlacesRepositoryImpl.swift +++ b/iphone/Maps/Tourism/Data/Repositories/PlacesRepositoryImpl.swift @@ -39,13 +39,15 @@ class PlacesRepositoryImpl: PlacesRepository { func downloadAllData() async throws { do { let hashes = hashesPersistenceController.getHashes() - let favoritesDto = try await placesService.getFavorites() if(hashes.isEmpty) { downloadProgress.send(DownloadProgress.loading) - let allData = try await placesService.getAllPlaces() - // get data + // download all data + let allData = try await placesService.getAllPlaces() + let favoritesDto = try await placesService.getFavorites() + + // patch data let favorites = favoritesDto.data.map { placeDto in placeDto.toPlaceFull(isFavorite: true) } diff --git a/iphone/Maps/Tourism/Domain/Models/PlaceLocation.swift b/iphone/Maps/Tourism/Domain/Models/PlaceLocation.swift index 747aa5bad5..4bb8543bc0 100644 --- a/iphone/Maps/Tourism/Domain/Models/PlaceLocation.swift +++ b/iphone/Maps/Tourism/Domain/Models/PlaceLocation.swift @@ -1,9 +1,16 @@ import Foundation -struct PlaceLocation: Codable { - let name: String - let lat: Double - let lon: Double +@objc(PlaceLocation) +class PlaceLocation: NSObject, Codable { + @objc let name: String + @objc let lat: Double + @objc let lon: Double + + init(name: String, lat: Double, lon: Double) { + self.name = name + self.lat = lat + self.lon = lon + } func toCoordinatesEntity() -> CoordinatesEntity { return CoordinatesEntity(latitude: lat, longitude: lon) diff --git a/iphone/Maps/Tourism/Presentation/Home/Screens/Categories/CategoriesViewController.swift b/iphone/Maps/Tourism/Presentation/Home/Screens/Categories/CategoriesViewController.swift index 2492a4ba3a..ac3f951ae9 100644 --- a/iphone/Maps/Tourism/Presentation/Home/Screens/Categories/CategoriesViewController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/Screens/Categories/CategoriesViewController.swift @@ -3,10 +3,20 @@ import SwiftUI class CategoriesViewController: UIViewController { private var categoriesVM: CategoriesViewModel private var searchVM: SearchViewModel + private var goToMap: () -> Void + private let onCreateRoute: (PlaceLocation) -> Void - init(categoriesVM: CategoriesViewModel, searchVM: SearchViewModel) { + init( + categoriesVM: CategoriesViewModel, + searchVM: SearchViewModel, + goToMap: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.categoriesVM = categoriesVM self.searchVM = searchVM + self.goToMap = goToMap + self.onCreateRoute = onCreateRoute + super.init( nibName: nil, bundle: nil @@ -25,12 +35,21 @@ class CategoriesViewController: UIViewController { categoriesVM: categoriesVM, goToSearchScreen: { query in self.searchVM.query = query - let destinationVC = SearchViewController(searchVM: self.searchVM) + let destinationVC = SearchViewController( + searchVM: self.searchVM, + goToMap: self.goToMap, + onCreateRoute: self.onCreateRoute + ) self.navigationController?.pushViewController(destinationVC, animated: true) }, goToPlaceScreen: { id in - self.goToPlaceScreen(id: id) - } + self.goToPlaceScreen( + id: id, + onMapClick: self.goToMap, + onCreateRoute: self.onCreateRoute + ) + }, + goToMap: goToMap ) ) } @@ -40,13 +59,22 @@ struct CategoriesScreen: View { @ObservedObject var categoriesVM: CategoriesViewModel var goToSearchScreen: (String) -> Void var goToPlaceScreen: (Int64) -> Void + var goToMap: () -> Void var body: some View { ScrollView { VStack(alignment: .leading) { VerticalSpace(height: 16) VStack { - AppTopBar(title: L("categories")) + AppTopBar( + title: L("categories"), + actions: [ + TopBarActionData( + iconName: "map", + onClick: goToMap + ) + ] + ) AppSearchBar( query: $categoriesVM.query, diff --git a/iphone/Maps/Tourism/Presentation/Home/Screens/Favorites/FavoritesViewController.swift b/iphone/Maps/Tourism/Presentation/Home/Screens/Favorites/FavoritesViewController.swift index f69c35064a..dbfeb84a28 100644 --- a/iphone/Maps/Tourism/Presentation/Home/Screens/Favorites/FavoritesViewController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/Screens/Favorites/FavoritesViewController.swift @@ -2,8 +2,17 @@ import SwiftUI class FavoritesViewController: UIViewController { private var favoritesVM: FavoritesViewModel - init(favoritesVM: FavoritesViewModel) { + private var goToMap: () -> Void + private let onCreateRoute: (PlaceLocation) -> Void + + init( + favoritesVM: FavoritesViewModel, + goToMap: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.favoritesVM = favoritesVM + self.goToMap = goToMap + self.onCreateRoute = onCreateRoute super.init( nibName: nil, @@ -22,7 +31,11 @@ class FavoritesViewController: UIViewController { FavoritesScreen( favoritesVM: favoritesVM, goToPlaceScreen: { id in - self.goToPlaceScreen(id: id) + self.goToPlaceScreen( + id: id, + onMapClick: self.goToMap, + onCreateRoute: self.onCreateRoute + ) } ) ) diff --git a/iphone/Maps/Tourism/Presentation/Home/Screens/New Group/HomeViewController.swift b/iphone/Maps/Tourism/Presentation/Home/Screens/New Group/HomeViewController.swift index 63320382fc..35ce786d59 100644 --- a/iphone/Maps/Tourism/Presentation/Home/Screens/New Group/HomeViewController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/Screens/New Group/HomeViewController.swift @@ -6,12 +6,24 @@ class HomeViewController: UIViewController { private var categoriesVM: CategoriesViewModel private var searchVM: SearchViewModel private var goToCategoriesTab: () -> Void + private var goToMap: () -> Void + private let onCreateRoute: (PlaceLocation) -> Void - init(homeVM: HomeViewModel, categoriesVM: CategoriesViewModel, searchVM: SearchViewModel, goToCategoriesTab: @escaping () -> Void) { + init( + homeVM: HomeViewModel, + categoriesVM: CategoriesViewModel, + searchVM: SearchViewModel, + goToCategoriesTab: @escaping () -> Void, + goToMap: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.homeVM = homeVM self.categoriesVM = categoriesVM self.searchVM = searchVM self.goToCategoriesTab = goToCategoriesTab + self.goToMap = goToMap + self.onCreateRoute = onCreateRoute + super.init( nibName: nil, bundle: nil @@ -33,12 +45,21 @@ class HomeViewController: UIViewController { goToCategoriesTab: goToCategoriesTab, goToSearchScreen: { query in self.searchVM.query = query - let destinationVC = SearchViewController(searchVM: self.searchVM) + let destinationVC = SearchViewController( + searchVM: self.searchVM, + goToMap: self.goToMap, + onCreateRoute: self.onCreateRoute + ) self.navigationController?.pushViewController(destinationVC, animated: false) }, goToPlaceScreen: { id in - self.goToPlaceScreen(id: id) - } + self.goToPlaceScreen( + id: id, + onMapClick: self.goToMap, + onCreateRoute: self.onCreateRoute + ) + }, + goToMap: goToMap ) ) } @@ -50,6 +71,7 @@ struct HomeScreen: View { var goToCategoriesTab: () -> Void var goToSearchScreen: (String) -> Void var goToPlaceScreen: (Int64) -> Void + var goToMap: () -> Void @State var top30: SingleChoiceItem? = SingleChoiceItem(id: 1, label: L("top30")) @@ -59,12 +81,25 @@ struct HomeScreen: View { ProgressView() Text(L("plz_wait_dowloading")) } + } else if (homeVM.downloadProgress == .error) { + VStack(spacing: 16) { + Text(L("download_failed")) + Text(homeVM.errorMessage) + } } else if (homeVM.downloadProgress == .success) { ScrollView { VStack (alignment: .leading) { VerticalSpace(height: 16) VStack { - AppTopBar(title: L("tjk")) + AppTopBar( + title: L("tjk"), + actions: [ + TopBarActionData( + iconName: "map", + onClick: goToMap + ) + ] + ) AppSearchBar( query: $homeVM.query, @@ -132,11 +167,6 @@ struct HomeScreen: View { } VerticalSpace(height: 32) } - } else if (homeVM.downloadProgress == .error) { - VStack(spacing: 16) { - Text(L("download_failed")) - Text(homeVM.errorMessage) - } } } } diff --git a/iphone/Maps/Tourism/Presentation/Home/Screens/Profile/PlaceDetails/PlaceViewController.swift b/iphone/Maps/Tourism/Presentation/Home/Screens/Profile/PlaceDetails/PlaceViewController.swift index 4c6fdbad1e..c25128eab2 100644 --- a/iphone/Maps/Tourism/Presentation/Home/Screens/Profile/PlaceDetails/PlaceViewController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/Screens/Profile/PlaceDetails/PlaceViewController.swift @@ -2,9 +2,18 @@ import SwiftUI class PlaceViewController: UIViewController { let placeId: Int64 + let onMapClick: () -> Void + let onCreateRoute: (PlaceLocation) -> Void - init(placeId: Int64) { + init( + placeId: Int64, + onMapClick: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.placeId = placeId + self.onMapClick = onMapClick + self.onCreateRoute = onCreateRoute + super.init( nibName: nil, bundle: nil @@ -32,7 +41,9 @@ class PlaceViewController: UIViewController { id: placeId, showBottomBar: { self.tabBarController?.tabBar.isHidden = false - } + }, + onMapClick: onMapClick, + onCreateRoute: onCreateRoute )) } } @@ -42,15 +53,25 @@ struct PlaceScreen: View { let reviewsVM: ReviewsViewModel let id: Int64 let showBottomBar: () -> Void + let onMapClick: () -> Void + let onCreateRoute: (PlaceLocation) -> Void @State private var selectedTab = 0 @Environment(\.presentationMode) var presentationMode: Binding - init(placeVM: PlaceViewModel, id: Int64, showBottomBar: @escaping () -> Void) { + init( + placeVM: PlaceViewModel, + id: Int64, + showBottomBar: @escaping () -> Void, + onMapClick: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.placeVM = placeVM self.id = id self.showBottomBar = showBottomBar + self.onMapClick = onMapClick + self.onCreateRoute = onCreateRoute self.reviewsVM = ReviewsViewModel( reviewsRepository: ReviewsRepositoryImpl( @@ -76,9 +97,7 @@ struct PlaceScreen: View { onFavoriteChanged: { isFavorite in placeVM.toggleFavorite(for: place.id, isFavorite: isFavorite) }, - onMapClick: { - // TODO: Cmon - } + onMapClick: onMapClick ) VStack { @@ -89,7 +108,9 @@ struct PlaceScreen: View { DescriptionScreen( description: place.description, onCreateRoute: { - // TODO: cmon + if let location = place.placeLocation { + onCreateRoute(location) + } } ) .tag(0) diff --git a/iphone/Maps/Tourism/Presentation/Home/Screens/Search/SearchViewController.swift b/iphone/Maps/Tourism/Presentation/Home/Screens/Search/SearchViewController.swift index f56297cd67..bf2e6f1530 100644 --- a/iphone/Maps/Tourism/Presentation/Home/Screens/Search/SearchViewController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/Screens/Search/SearchViewController.swift @@ -2,9 +2,19 @@ import SwiftUI class SearchViewController: UIViewController { private var searchVM: SearchViewModel + private var goToMap: () -> Void + private let onCreateRoute: (PlaceLocation) -> Void - init(searchVM: SearchViewModel) { + init( + searchVM: SearchViewModel, + goToMap: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { self.searchVM = searchVM + self.goToMap = goToMap + self.onCreateRoute = onCreateRoute + + super.init( nibName: nil, bundle: nil @@ -21,7 +31,11 @@ class SearchViewController: UIViewController { integrateSwiftUIScreen(SearchScreen( searchVM: searchVM, goToPlaceScreen: { id in - self.goToPlaceScreen(id: id) + self.goToPlaceScreen( + id: id, + onMapClick: self.goToMap, + onCreateRoute: self.onCreateRoute + ) } )) } diff --git a/iphone/Maps/Tourism/Presentation/Home/TabBarController.swift b/iphone/Maps/Tourism/Presentation/Home/TabBarController.swift index bb4fc41082..cd080fa8a0 100644 --- a/iphone/Maps/Tourism/Presentation/Home/TabBarController.swift +++ b/iphone/Maps/Tourism/Presentation/Home/TabBarController.swift @@ -15,6 +15,19 @@ class TabBarController: UITabBarController { hidesBottomBarWhenPushed = true + // navigation functions + let goToCategoriesTab = { self.selectedIndex = 1 } + let goToMap = { + self.dismiss(animated: true) + } + let goToAuth = { + self.performSegue(withIdentifier: "TourismMain2Auth", sender: nil) + } + let goToMapAndCreateRoute: (PlaceLocation) -> Void = { location in + UserPreferences.shared.setLocation(value: location) + self.dismiss(animated: true) + } + // creating tabs let homeTab = UITabBarItem(title: L("home"), image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house.fill")) let categoriesTab = UITabBarItem(title: L("categories"), image: UIImage(systemName: "list.bullet.rectangle"), selectedImage: UIImage(systemName: "list.bullet.rectangle.fill")) @@ -65,25 +78,28 @@ class TabBarController: UITabBarController { authRepository: authRepository, userPreferences: UserPreferences.shared ) - profileVM.onSignOutCompleted = { - self.performSegue(withIdentifier: "TourismMain2Auth", sender: nil) - } - - // navigation functions - let goToCategoriesTab = { self.selectedIndex = 1 } + profileVM.onSignOutCompleted = goToAuth // creating ViewControllers let homeVC = HomeViewController( homeVM: homeVM, categoriesVM: categoriesVM, searchVM: searchVM, - goToCategoriesTab: goToCategoriesTab + goToCategoriesTab: goToCategoriesTab, + goToMap: goToMap, + onCreateRoute: goToMapAndCreateRoute ) let categoriesVC = CategoriesViewController( categoriesVM: categoriesVM, - searchVM: searchVM + searchVM: searchVM, + goToMap: goToMap, + onCreateRoute: goToMapAndCreateRoute + ) + let favoritesVC = FavoritesViewController( + favoritesVM: favoritesVM, + goToMap: goToMap, + onCreateRoute: goToMapAndCreateRoute ) - let favoritesVC = FavoritesViewController(favoritesVM: favoritesVM) let profileVC = ProfileViewController(profileVM: profileVM) // setting up navigation diff --git a/iphone/Maps/Tourism/Presentation/Home/TourismMain.storyboard b/iphone/Maps/Tourism/Presentation/Home/TourismMain.storyboard index c1ec2cedc9..e25e615b25 100644 --- a/iphone/Maps/Tourism/Presentation/Home/TourismMain.storyboard +++ b/iphone/Maps/Tourism/Presentation/Home/TourismMain.storyboard @@ -1,9 +1,9 @@ - + - + diff --git a/iphone/Maps/Tourism/Presentation/Utils/NavigationUtils.swift b/iphone/Maps/Tourism/Presentation/Utils/NavigationUtils.swift index 34f3dee15d..01db71d268 100644 --- a/iphone/Maps/Tourism/Presentation/Utils/NavigationUtils.swift +++ b/iphone/Maps/Tourism/Presentation/Utils/NavigationUtils.swift @@ -1,6 +1,14 @@ extension UIViewController { - func goToPlaceScreen(id: Int64) { - let destinationVC = PlaceViewController(placeId: id) + func goToPlaceScreen( + id: Int64, + onMapClick: @escaping () -> Void, + onCreateRoute: @escaping (PlaceLocation) -> Void + ) { + let destinationVC = PlaceViewController( + placeId: id, + onMapClick: onMapClick, + onCreateRoute: onCreateRoute + ) self.navigationController?.pushViewController(destinationVC, animated: false) self.tabBarController?.tabBar.isHidden = true } diff --git a/iphone/Maps/UI/PlacePage/Components/ActionBarViewController.swift b/iphone/Maps/UI/PlacePage/Components/ActionBarViewController.swift index 47c39c1533..3008c85643 100644 --- a/iphone/Maps/UI/PlacePage/Components/ActionBarViewController.swift +++ b/iphone/Maps/UI/PlacePage/Components/ActionBarViewController.swift @@ -89,17 +89,6 @@ class ActionBarViewController: UIViewController { } private func configButton1() { - if let mapNodeAttributes = placePageData.mapNodeAttributes { - switch mapNodeAttributes.nodeStatus { - case .onDiskOutOfDate, .onDisk, .undefined: - break - case .downloading, .applying, .inQueue, .error, .notDownloaded, .partly: - visibleButtons.append(.download) - return - @unknown default: - fatalError() - } - } var buttons: [ActionBarButtonType] = [] if isRoutePlanning { buttons.append(.routeFrom)