From 54798069b30de479fd804e44a1a036ea3db1c753 Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 5 Oct 2016 13:54:11 +0300 Subject: [ios] New place page --- iphone/Maps/Bookmarks/BookmarksRootVC.mm | 2 +- iphone/Maps/Bookmarks/BookmarksVC.h | 4 +- iphone/Maps/Bookmarks/BookmarksVC.mm | 6 +- iphone/Maps/Bookmarks/SelectSetVC.mm | 10 +- .../MapViewControls/MWMMapViewControlsManager.h | 9 +- .../MapViewControls/MWMMapViewControlsManager.mm | 41 +- .../BookmarksTab/MWMSearchBookmarksCell.mm | 2 +- iphone/Maps/Classes/MWMActionBarButton.mm | 4 +- iphone/Maps/Classes/MWMBasePlacePageView.mm | 1 + iphone/Maps/Classes/MWMBookmarkCell.h | 12 + iphone/Maps/Classes/MWMBookmarkCell.mm | 216 +++++++++ iphone/Maps/Classes/MWMDirectionView.h | 2 + iphone/Maps/Classes/MWMDirectionView.mm | 35 +- iphone/Maps/Classes/MWMEditBookmarkController.h | 2 + iphone/Maps/Classes/MWMEditBookmarkController.mm | 68 ++- iphone/Maps/Classes/MWMOpeningHours.h | 32 ++ iphone/Maps/Classes/MWMOpeningHours.mm | 128 +++++ iphone/Maps/Classes/MWMOpeningHoursCell.h | 10 + iphone/Maps/Classes/MWMOpeningHoursCell.mm | 200 ++++++++ iphone/Maps/Classes/MWMPPView.h | 31 ++ iphone/Maps/Classes/MWMPPView.mm | 101 ++++ iphone/Maps/Classes/MWMPlacePage.mm | 2 +- iphone/Maps/Classes/MWMPlacePageActionBar.h | 18 + iphone/Maps/Classes/MWMPlacePageActionBar.mm | 79 ++- iphone/Maps/Classes/MWMPlacePageActionBar.xib | 56 +++ iphone/Maps/Classes/MWMPlacePageBookmarkCell.h | 9 +- iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm | 1 + iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h | 8 + iphone/Maps/Classes/MWMPlacePageButtonCell.h | 11 +- iphone/Maps/Classes/MWMPlacePageButtonCell.mm | 61 ++- .../Maps/Classes/MWMPlacePageCellUpdateProtocol.h | 5 + iphone/Maps/Classes/MWMPlacePageData.h | 103 ++++ iphone/Maps/Classes/MWMPlacePageData.mm | 406 ++++++++++++++++ iphone/Maps/Classes/MWMPlacePageEntity.h | 7 +- iphone/Maps/Classes/MWMPlacePageEntity.mm | 23 +- iphone/Maps/Classes/MWMPlacePageInfoCell.h | 6 +- iphone/Maps/Classes/MWMPlacePageInfoCell.mm | 113 ++++- iphone/Maps/Classes/MWMPlacePageLayout.h | 42 ++ iphone/Maps/Classes/MWMPlacePageLayout.mm | 530 +++++++++++++++++++++ iphone/Maps/Classes/MWMPlacePageManager.h | 5 + iphone/Maps/Classes/MWMPlacePageManager.mm | 335 +++++++++++++ iphone/Maps/Classes/MWMPlacePagePreviewCell.h | 24 + iphone/Maps/Classes/MWMPlacePagePreviewCell.mm | 426 +++++++++++++++++ iphone/Maps/Classes/MWMPlacePageProtocol.h | 58 +++ iphone/Maps/Classes/MWMPlacePageViewManager.h | 7 +- iphone/Maps/Classes/MWMPlacePageViewManager.mm | 45 +- iphone/Maps/Classes/MapViewController.h | 2 + iphone/Maps/Classes/MapViewController.mm | 21 +- iphone/Maps/Classes/PlacePageActionBar.xib | 56 --- .../Maps/Classes/Share/MWMActivityViewController.h | 4 +- .../Classes/Share/MWMActivityViewController.mm | 4 +- iphone/Maps/Classes/Share/MWMShareActivityItem.h | 21 +- iphone/Maps/Classes/Share/MWMShareActivityItem.mm | 40 +- 53 files changed, 3210 insertions(+), 234 deletions(-) create mode 100644 iphone/Maps/Classes/MWMBookmarkCell.h create mode 100644 iphone/Maps/Classes/MWMBookmarkCell.mm create mode 100644 iphone/Maps/Classes/MWMOpeningHours.h create mode 100644 iphone/Maps/Classes/MWMOpeningHours.mm create mode 100644 iphone/Maps/Classes/MWMOpeningHoursCell.h create mode 100644 iphone/Maps/Classes/MWMOpeningHoursCell.mm create mode 100644 iphone/Maps/Classes/MWMPPView.h create mode 100644 iphone/Maps/Classes/MWMPPView.mm create mode 100644 iphone/Maps/Classes/MWMPlacePageActionBar.xib create mode 100644 iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h create mode 100644 iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h create mode 100644 iphone/Maps/Classes/MWMPlacePageData.h create mode 100644 iphone/Maps/Classes/MWMPlacePageData.mm create mode 100644 iphone/Maps/Classes/MWMPlacePageLayout.h create mode 100644 iphone/Maps/Classes/MWMPlacePageLayout.mm create mode 100644 iphone/Maps/Classes/MWMPlacePageManager.h create mode 100644 iphone/Maps/Classes/MWMPlacePageManager.mm create mode 100644 iphone/Maps/Classes/MWMPlacePagePreviewCell.h create mode 100644 iphone/Maps/Classes/MWMPlacePagePreviewCell.mm create mode 100644 iphone/Maps/Classes/MWMPlacePageProtocol.h delete mode 100644 iphone/Maps/Classes/PlacePageActionBar.xib (limited to 'iphone/Maps') diff --git a/iphone/Maps/Bookmarks/BookmarksRootVC.mm b/iphone/Maps/Bookmarks/BookmarksRootVC.mm index b2600575b4..77f2ad1ec0 100644 --- a/iphone/Maps/Bookmarks/BookmarksRootVC.mm +++ b/iphone/Maps/Bookmarks/BookmarksRootVC.mm @@ -219,7 +219,7 @@ } else { - BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:indexPath.row]; + BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:static_cast(indexPath.row)]; [self.navigationController pushViewController:bvc animated:YES]; } } diff --git a/iphone/Maps/Bookmarks/BookmarksVC.h b/iphone/Maps/Bookmarks/BookmarksVC.h index 52a78d376d..f19b207856 100644 --- a/iphone/Maps/Bookmarks/BookmarksVC.h +++ b/iphone/Maps/Bookmarks/BookmarksVC.h @@ -2,9 +2,9 @@ @interface BookmarksVC : MWMTableViewController { - size_t m_categoryIndex; + int m_categoryIndex; } -- (instancetype)initWithCategory:(size_t)index; +- (instancetype)initWithCategory:(int)index; @end diff --git a/iphone/Maps/Bookmarks/BookmarksVC.mm b/iphone/Maps/Bookmarks/BookmarksVC.mm index b08c08a68e..94ddc78439 100644 --- a/iphone/Maps/Bookmarks/BookmarksVC.mm +++ b/iphone/Maps/Bookmarks/BookmarksVC.mm @@ -38,7 +38,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi @implementation BookmarksVC -- (instancetype)initWithCategory:(size_t)index +- (instancetype)initWithCategory:(int)index { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) @@ -236,7 +236,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi // Same as "Close". MapViewController * mapVC = self.navigationController.viewControllers.firstObject; mapVC.controlsManager.searchHidden = YES; - f.ShowBookmark(BookmarkAndCategory(m_categoryIndex, indexPath.row)); + f.ShowBookmark({static_cast(indexPath.row), m_categoryIndex}); [self.navigationController popToRootViewControllerAnimated:YES]; } } @@ -294,7 +294,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi } else { - BookmarkAndCategory bookmarkAndCategory = BookmarkAndCategory(m_categoryIndex, indexPath.row); + BookmarkAndCategory bookmarkAndCategory{static_cast(indexPath.row), m_categoryIndex}; NSValue * value = [NSValue valueWithBytes:&bookmarkAndCategory objCType:@encode(BookmarkAndCategory)]; [[NSNotificationCenter defaultCenter] postNotificationName:BOOKMARK_DELETED_NOTIFICATION object:value]; BookmarkCategory::Guard guard(*cat); diff --git a/iphone/Maps/Bookmarks/SelectSetVC.mm b/iphone/Maps/Bookmarks/SelectSetVC.mm index f5b8d29b4f..c3952b5cb4 100644 --- a/iphone/Maps/Bookmarks/SelectSetVC.mm +++ b/iphone/Maps/Bookmarks/SelectSetVC.mm @@ -35,7 +35,7 @@ [super viewDidLoad]; NSAssert(self.category, @"Category can't be nil!"); NSAssert(self.delegate, @"Delegate can't be nil!"); - NSAssert(IsValid(m_bac), @"Invalid BookmarkAndCategory's instance!"); + NSAssert(m_bac.IsValid(), @"Invalid BookmarkAndCategory's instance!"); self.title = L(@"bookmark_sets"); } @@ -67,7 +67,7 @@ if (cat) cell.textLabel.text = @(cat->GetName().c_str()); - if (m_bac.first == indexPath.row) + if (m_bac.m_categoryIndex == indexPath.row) cell.accessoryType = UITableViewCellAccessoryCheckmark; else cell.accessoryType = UITableViewCellAccessoryNone; @@ -85,11 +85,11 @@ - (void)moveBookmarkToSetWithIndex:(int)setIndex { BookmarkAndCategory bac; - bac.second = static_cast(GetFramework().MoveBookmark(m_bac.second, m_bac.first, setIndex)); - bac.first = setIndex; + bac.m_bookmarkIndex = static_cast(GetFramework().MoveBookmark(m_bac.m_bookmarkIndex, m_bac.m_categoryIndex, setIndex)); + bac.m_categoryIndex = setIndex; m_bac = bac; - BookmarkCategory const * category = GetFramework().GetBookmarkManager().GetBmCategory(bac.first); + BookmarkCategory const * category = GetFramework().GetBookmarkManager().GetBmCategory(bac.m_categoryIndex); self.category = @(category->GetName().c_str()); } diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h index 9f218003f9..5af2b8b449 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h @@ -9,6 +9,7 @@ @class MapViewController; @class MWMPlacePageEntity; +@protocol MWMFeatureHolder; @interface MWMMapViewControlsManager : NSObject @@ -20,8 +21,8 @@ @property(nonatomic) MWMBottomMenuState menuState; @property(nonatomic) MWMBottomMenuState menuRestoreState; @property(nonatomic, readonly) MWMNavigationDashboardState navigationState; -@property(nonatomic, readonly) MWMPlacePageEntity * placePageEntity; @property(nonatomic) BOOL searchHidden; +@property(nonatomic) BOOL isDirectionViewHidden; - (instancetype)init __attribute__((unavailable("init is not available"))); - (instancetype)initWithParentController:(MapViewController *)controller; @@ -40,8 +41,6 @@ #pragma mark - MWMPlacePageViewManager -@property(nonatomic, readonly) BOOL isDirectionViewShown; - - (void)dismissPlacePage; - (void)showPlacePage:(place_page::Info const &)info; - (void)addPlacePageViews:(NSArray *)views; @@ -68,4 +67,8 @@ - (void)searchFrameUpdated:(CGRect)frame; - (void)searchText:(NSString *)text forInputLocale:(NSString *)locale; +#pragma mark - MWMFeatureHolder + +- (id)featureHolder; + @end diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm index f5877cf524..06f83de0a5 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm @@ -10,6 +10,7 @@ #import "MWMFrameworkListener.h" #import "MWMObjectsCategorySelectorController.h" #import "MWMPlacePageEntity.h" +#import "MWMPlacePageManager.h" #import "MWMPlacePageViewManager.h" #import "MWMRoutePreview.h" #import "MWMRouter.h" @@ -42,7 +43,7 @@ extern NSString * const kAlohalyticsTapEventKey; @property(nonatomic) MWMSideButtons * sideButtons; @property(nonatomic) MWMBottomMenuViewController * menuController; -@property(nonatomic) MWMPlacePageViewManager * placePageManager; +@property(nonatomic) id placePageManager; @property(nonatomic) MWMNavigationDashboardManager * navigationManager; @property(nonatomic) MWMSearchManager * searchManager; @@ -84,7 +85,7 @@ extern NSString * const kAlohalyticsTapEventKey; searchManagerState != MWMSearchManagerStateHidden) || self.navigationState == MWMNavigationDashboardStatePlanning || self.navigationState == MWMNavigationDashboardStateReady || - self.menuState == MWMBottomMenuStateActive || self.isDirectionViewShown || + self.menuState == MWMBottomMenuStateActive || !self.isDirectionViewHidden || (isNightMode && self.navigationState != MWMNavigationDashboardStateHidden) || MapsAppDelegate.theApp.routingPlaneMode != MWMRoutingPlaneModeNone; return (isLight || (!isLight && isNightMode)) ? UIStatusBarStyleLightContent @@ -292,12 +293,19 @@ extern NSString * const kAlohalyticsTapEventKey; { if (IPAD) return; - CGSize const ownerViewSize = self.ownerController.view.size; - if (ownerViewSize.width > ownerViewSize.height) + if (isIOS7) { - CGFloat const leftBound = frame.origin.x + frame.size.width; - self.menuController.leftBound = leftBound; - [MWMNavigationDashboardManager manager].leftBound = leftBound; + CGSize const ownerViewSize = self.ownerController.view.size; + if (ownerViewSize.width > ownerViewSize.height) + { + CGFloat const leftBound = frame.origin.x + frame.size.width; + self.menuController.leftBound = leftBound; + [MWMNavigationDashboardManager manager].leftBound = leftBound; + } + else + { + [self.sideButtons setBottomBound:frame.origin.y]; + } } else { @@ -459,11 +467,13 @@ extern NSString * const kAlohalyticsTapEventKey; return _menuController; } -- (MWMPlacePageViewManager *)placePageManager +- (id)placePageManager { + auto const PlacePageClass = isIOS7 || IPAD ? [MWMPlacePageViewManager class] : [MWMPlacePageManager class]; + if (!_placePageManager) _placePageManager = - [[MWMPlacePageViewManager alloc] initWithViewController:self.ownerController]; + [[PlacePageClass alloc] initWithViewController:self.ownerController]; return _placePageManager; } @@ -526,12 +536,6 @@ extern NSString * const kAlohalyticsTapEventKey; } - (MWMNavigationDashboardState)navigationState { return self.navigationManager.state; } -- (MWMPlacePageEntity *)placePageEntity { return self.placePageManager.entity; } -- (BOOL)isDirectionViewShown -{ - return _placePageManager ? _placePageManager.isDirectionViewShown : NO; -} - - (void)setTopBound:(CGFloat)topBound { if (IPAD) @@ -558,4 +562,11 @@ extern NSString * const kAlohalyticsTapEventKey; searchHidden ? MWMSearchManagerStateHidden : MWMSearchManagerStateDefault; } +#pragma mark - MWMFeatureHolder + +- (id)featureHolder +{ + return self.placePageManager; +} + @end diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm index 54cf32d377..3d2ac72aa7 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm @@ -57,7 +57,7 @@ - (IBAction)openBookmarks { - BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:self.index]; + BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:static_cast(self.index)]; UINavigationController * rootVC = (UINavigationController *)UIApplication.sharedApplication.delegate.window.rootViewController; [rootVC pushViewController:bvc animated:YES]; } diff --git a/iphone/Maps/Classes/MWMActionBarButton.mm b/iphone/Maps/Classes/MWMActionBarButton.mm index 7e67a3bf1a..361ec00a44 100644 --- a/iphone/Maps/Classes/MWMActionBarButton.mm +++ b/iphone/Maps/Classes/MWMActionBarButton.mm @@ -1,3 +1,4 @@ +#import "Common.h" #import "MWMActionBarButton.h" #import "MWMButton.h" #import "UIColor+MapsMeColor.h" @@ -95,7 +96,8 @@ NSString * titleForButton(EButton type, BOOL isSelected) button.delegate = delegate; button.type = type; [view addSubview:button]; - button.autoresizingMask = UIViewAutoresizingNone; + if (isIOS7) + button.autoresizingMask = UIViewAutoresizingNone; [button configButton:isSelected]; } diff --git a/iphone/Maps/Classes/MWMBasePlacePageView.mm b/iphone/Maps/Classes/MWMBasePlacePageView.mm index 16e3b99c65..9c56f2f9db 100644 --- a/iphone/Maps/Classes/MWMBasePlacePageView.mm +++ b/iphone/Maps/Classes/MWMBasePlacePageView.mm @@ -6,6 +6,7 @@ #import "MWMPlacePage.h" #import "MWMPlacePageActionBar.h" #import "MWMPlacePageBookmarkCell.h" +#import "MWMPlacePageBookmarkDelegate.h" #import "MWMPlacePageButtonCell.h" #import "MWMPlacePageEntity.h" #import "MWMPlacePageInfoCell.h" diff --git a/iphone/Maps/Classes/MWMBookmarkCell.h b/iphone/Maps/Classes/MWMBookmarkCell.h new file mode 100644 index 0000000000..a9c30ab996 --- /dev/null +++ b/iphone/Maps/Classes/MWMBookmarkCell.h @@ -0,0 +1,12 @@ +#import "MWMTableViewCell.h" + +@protocol MWMPlacePageButtonsProtocol, MWMPlacePageCellUpdateProtocol; + +@interface MWMBookmarkCell : MWMTableViewCell + +- (void)configureWithText:(NSString *)text + updateCellDelegate:(id)updateCellDelegate + editBookmarkDelegate:(id)editBookmarkDelegate + isHTML:(BOOL)isHTML; + +@end diff --git a/iphone/Maps/Classes/MWMBookmarkCell.mm b/iphone/Maps/Classes/MWMBookmarkCell.mm new file mode 100644 index 0000000000..4698529b7d --- /dev/null +++ b/iphone/Maps/Classes/MWMBookmarkCell.mm @@ -0,0 +1,216 @@ +#import "MWMPlacePageCellUpdateProtocol.h" +#import "MWMBookmarkCell.h" + +#import "MWMPlacePageProtocol.h" +#import "UIColor+MapsMeColor.h" +#import "UIFont+MapsMeFonts.h" + +namespace +{ +void * kContext = &kContext; +NSString * const kTextViewContentSizeKeyPath = @"contentSize"; + +} // namespace + +@interface MWMBookmarkCell () + +@property(weak, nonatomic) IBOutlet UITextView * textView; +@property(weak, nonatomic) IBOutlet UIImageView * spinner; +@property(weak, nonatomic) IBOutlet UIButton * editButton; + +@property(weak, nonatomic) IBOutlet UIImageView * gradientView; + +@property(nonatomic) IBOutlet NSLayoutConstraint * textViewHeight; +@property(weak, nonatomic) IBOutlet NSLayoutConstraint * moreButtonHeight; +@property(nonatomic) IBOutlet NSLayoutConstraint * textViewZeroHeight; + +@property(weak, nonatomic) id updateCellDelegate; +@property(weak, nonatomic) id editBookmarkDelegate; + +@property(copy, nonatomic) NSAttributedString * attributedHTML; + +@property (nonatomic) BOOL isOpen; + +@end + +@implementation MWMBookmarkCell + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [self registerObserver]; +} + +- (void)dealloc +{ + [self unregisterObserver]; +} + +- (void)unregisterObserver +{ + [self.textView removeObserver:self forKeyPath:kTextViewContentSizeKeyPath context:kContext]; +} + +- (void)registerObserver +{ + [self.textView addObserver:self forKeyPath:kTextViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == kContext) + { + NSValue * s = change[@"new"]; + CGFloat const height = s.CGSizeValue.height; + auto const boundedHeight = self.textViewHeight.constant; + + if (height < boundedHeight || self.isOpen) + [self stateOpen:YES]; + else if (height > boundedHeight && !self.isOpen) + [self stateOpen:NO]; + + [self setNeedsLayout]; + [self.updateCellDelegate updateCellWithForceReposition:NO]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (void)configureWithText:(NSString *)text + updateCellDelegate:(id)updateCellDelegate + editBookmarkDelegate:(id)editBookmarkDelegate + isHTML:(BOOL)isHTML +{ + self.attributedHTML = nil; + self.isOpen = NO; + self.textViewHeight.active = NO; + self.textViewZeroHeight.active = NO; + self.updateCellDelegate = updateCellDelegate; + self.editBookmarkDelegate = editBookmarkDelegate; + + if (!text.length) + { + [self configWithEmptyDescription]; + } + else if (isHTML) + { + [self configHTML:text]; + } + else + { + [self configPlain:text]; + } +} + +- (void)configWithEmptyDescription +{ + [self stopSpinner]; + + [self stateOpen:YES]; + self.textViewZeroHeight.active = YES; +} + +- (void)configHTML:(NSString *)text +{ + if (self.attributedHTML) + { + [self stopSpinner]; + self.textViewZeroHeight.active = NO; + self.textView.attributedText = self.attributedHTML; + if (fabs(self.textView.contentSize.height - 0.5) < 1) + [self.textView sizeToFit]; + } + else + { + self.textViewZeroHeight.active = YES; + [self startSpinner]; + [self stateOpen:YES]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSDictionary * attr = @{ + NSForegroundColorAttributeName : [UIColor blackPrimaryText], + NSFontAttributeName : [UIFont regular16] + }; + NSError * error = nil; + NSMutableAttributedString * str = [[NSMutableAttributedString alloc] + initWithData:[text dataUsingEncoding:NSUnicodeStringEncoding] + options:@{ + NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType + } + documentAttributes:nil + error:&error]; + if (error) + { + // If we failed while attempting to render html than just show plain text in bookmark. + // Ussualy there is a problem only in iOS7. + self.attributedHTML = [[NSAttributedString alloc] initWithString:text attributes:attr]; + } + else + { + [str addAttributes:attr range:{0, str.length}]; + self.attributedHTML = str; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + + [self configHTML:nil]; + }); + }); + } +} + +- (void)configPlain:(NSString *)text +{ + [self stopSpinner]; + self.textView.text = text; +} + +- (void)stateOpen:(BOOL)isOpen +{ + self.moreButtonHeight.constant = isOpen ? 0 : 33; + self.textViewHeight.active = !isOpen; + self.gradientView.hidden = isOpen; +} + +- (void)startSpinner +{ +// self.textViewZeroHeight.active = YES; + self.editButton.hidden = YES; + NSUInteger const animationImagesCount = 12; + NSMutableArray * animationImages = [NSMutableArray arrayWithCapacity:animationImagesCount]; + NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light"; + for (NSUInteger i = 0; i < animationImagesCount; ++i) + animationImages[i] = + [UIImage imageNamed:[NSString stringWithFormat:@"Spinner_%@_%@", @(i + 1), postfix]]; + + self.spinner.animationDuration = 0.8; + self.spinner.animationImages = animationImages; + self.spinner.hidden = NO; + [self.spinner startAnimating]; +} + +- (void)stopSpinner +{ + [self.spinner stopAnimating]; + self.editButton.hidden = NO; + self.spinner.hidden = YES; +} + +- (IBAction)moreTap +{ + self.isOpen = YES; + [self stateOpen:YES]; + [self setNeedsLayout]; +// [self.delegate updateCellWithForceReposition:NO]; +} + +- (IBAction)editTap +{ + [self.editBookmarkDelegate editBookmark]; +} + +@end diff --git a/iphone/Maps/Classes/MWMDirectionView.h b/iphone/Maps/Classes/MWMDirectionView.h index c782d78538..e9f8d99ee9 100644 --- a/iphone/Maps/Classes/MWMDirectionView.h +++ b/iphone/Maps/Classes/MWMDirectionView.h @@ -13,6 +13,8 @@ - (instancetype)initWithManager:(MWMPlacePageViewManager *)manager; - (void)setDirectionArrowTransform:(CGAffineTransform)transform; +- (void)show; + - (instancetype)initWithCoder:(NSCoder *)aDecoder __attribute__((unavailable("initWithCoder is not available"))); - (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("initWithFrame is not available"))); - (instancetype)init __attribute__((unavailable("init is not available"))); diff --git a/iphone/Maps/Classes/MWMDirectionView.mm b/iphone/Maps/Classes/MWMDirectionView.mm index bbbc956bee..a5625a0796 100644 --- a/iphone/Maps/Classes/MWMDirectionView.mm +++ b/iphone/Maps/Classes/MWMDirectionView.mm @@ -1,3 +1,6 @@ +#import "MapsAppDelegate.h" +#import "MapViewController.h" +#import "MWMMapViewControlsManager.h" #import "MWMDirectionView.h" #import "MWMPlacePageViewManager.h" #import "UIFont+MapsMeFonts.h" @@ -34,6 +37,36 @@ static CGFloat const kDirectionArrowSide = IPAD ? 260. : 160.; self.directionArrow.autoresizingMask = UIViewAutoresizingNone; } +- (void)show +{ + UIView * superview = [MapsAppDelegate theApp].mapViewController.view; + [superview addSubview:self]; + [superview endEditing:YES]; + [self setNeedsLayout]; +} + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + + MapsAppDelegate * app = [MapsAppDelegate theApp]; + + MWMMapViewControlsManager * manager = [MWMMapViewControlsManager manager]; + + if (self.superview) + { + [app disableStandby]; + manager.isDirectionViewHidden = NO; + } + else + { + [app enableStandby]; + manager.isDirectionViewHidden = YES; + } + + [app.mapViewController updateStatusBarStyle]; +} + - (void)layoutSubviews { UIView * superview = self.superview; @@ -122,7 +155,7 @@ static CGFloat const kDirectionArrowSide = IPAD ? 260. : 160.; { // Prevent super call to stop event propagation // [super touchesBegan:touches withEvent:event]; - [self.manager hideDirectionView]; + [self removeFromSuperview]; } @end diff --git a/iphone/Maps/Classes/MWMEditBookmarkController.h b/iphone/Maps/Classes/MWMEditBookmarkController.h index 261387e7ef..3e6ae72c9d 100644 --- a/iphone/Maps/Classes/MWMEditBookmarkController.h +++ b/iphone/Maps/Classes/MWMEditBookmarkController.h @@ -1,9 +1,11 @@ #import "MWMTableViewController.h" @class MWMPlacePageViewManager; +@class MWMPlacePageData; @interface MWMEditBookmarkController : MWMTableViewController +@property (weak, nonatomic) MWMPlacePageData * data; @property (nonatomic) MWMPlacePageViewManager * manager; @end diff --git a/iphone/Maps/Classes/MWMEditBookmarkController.mm b/iphone/Maps/Classes/MWMEditBookmarkController.mm index 8ef1de441d..6374b6a2fc 100644 --- a/iphone/Maps/Classes/MWMEditBookmarkController.mm +++ b/iphone/Maps/Classes/MWMEditBookmarkController.mm @@ -1,8 +1,10 @@ +#import "Common.h" #import "MWMBookmarkTitleCell.h" #import "MWMButtonCell.h" #import "MWMBookmarkColorViewController.h" #import "MWMEditBookmarkController.h" #import "MWMNoteCell.h" +#import "MWMPlacePageData.h" #import "MWMPlacePageEntity.h" #import "MWMPlacePageViewManager.h" #import "SelectSetVC.h" @@ -50,13 +52,23 @@ enum RowInMetaInfo - (void)viewDidLoad { [super viewDidLoad]; - NSAssert(self.manager, @"Entity can't be nil!"); - MWMPlacePageEntity * en = self.manager.entity; - self.cachedDescription = en.bookmarkDescription; - self.cachedTitle = en.bookmarkTitle; - self.cachedCategory = en.bookmarkCategory; - self.cachedColor = en.bookmarkColor; - m_cachedBac = en.bac; + NSAssert(self.manager || self.data, @"Entity and data can't be nil both!"); + if (isIOS7 || IPAD) + { + MWMPlacePageEntity * en = self.manager.entity; + self.cachedDescription = en.bookmarkDescription; + self.cachedTitle = en.bookmarkTitle; + self.cachedCategory = en.bookmarkCategory; + self.cachedColor = en.bookmarkColor; + m_cachedBac = en.bac; + } + else + { + self.cachedDescription = self.data.bookmarkDescription; + self.cachedTitle = self.data.externalTitle ? self.data.externalTitle : self.data.title; + self.cachedCategory = self.data.bookmarkCategory; + self.cachedColor = self.data.bookmarkColor; + } [self configNavBar]; [self registerCells]; @@ -95,14 +107,40 @@ enum RowInMetaInfo - (void)onSave { [self.view endEditing:YES]; - MWMPlacePageEntity * en = self.manager.entity; - en.bookmarkDescription = self.cachedDescription; - en.bookmarkColor = self.cachedColor; - en.bookmarkCategory = self.cachedCategory; - en.bookmarkTitle = self.cachedTitle; - en.bac = m_cachedBac; - [en synchronize]; - [self.manager reloadBookmark]; + if (isIOS7 || IPAD) + { + MWMPlacePageEntity * en = self.manager.entity; + en.bookmarkDescription = self.cachedDescription; + en.bookmarkColor = self.cachedColor; + en.bookmarkCategory = self.cachedCategory; + en.bookmarkTitle = self.cachedTitle; + en.bac = m_cachedBac; + [en synchronize]; + [self.manager reloadBookmark]; + } + else + { + Framework & f = GetFramework(); + auto const bac = self.data.bac; + BookmarkCategory * category = f.GetBmCategory(bac.m_categoryIndex); + if (!category) + return; + + { + BookmarkCategory::Guard guard(*category); + Bookmark * bookmark = + static_cast(guard.m_controller.GetUserMarkForEdit(bac.m_bookmarkIndex)); + if (!bookmark) + return; + + bookmark->SetType(self.cachedColor.UTF8String); + bookmark->SetDescription(self.cachedDescription.UTF8String); + bookmark->SetName(self.cachedTitle.UTF8String); + } + + category->SaveToKMLFile(); + f.UpdatePlacePageInfoForCurrentSelection(); + } [self backTap]; } diff --git a/iphone/Maps/Classes/MWMOpeningHours.h b/iphone/Maps/Classes/MWMOpeningHours.h new file mode 100644 index 0000000000..70558501c5 --- /dev/null +++ b/iphone/Maps/Classes/MWMOpeningHours.h @@ -0,0 +1,32 @@ +namespace osmoh +{ +struct Day +{ + Day(NSString * workingDays, NSString * workingTimes, NSString * breaks) : m_workingDays(workingDays), + m_workingTimes(workingTimes), + m_breaks(breaks) {} + + explicit Day(NSString * workingDays) : m_workingDays(workingDays), + m_isOpen(false) {} + + NSString * TodayTime() const + { + return m_workingTimes ? [NSString stringWithFormat:@"%@ %@", m_workingDays, m_workingTimes] : m_workingDays; + } + + NSString * m_workingDays; + NSString * m_workingTimes; + NSString * m_breaks; + bool m_isOpen = true; +}; + +} // namespace osmoh + +#include "std/string.hpp" +#include "std/vector.hpp" + +@interface MWMOpeningHours : NSObject + ++ (vector)processRawString:(NSString *)str; + +@end diff --git a/iphone/Maps/Classes/MWMOpeningHours.mm b/iphone/Maps/Classes/MWMOpeningHours.mm new file mode 100644 index 0000000000..62fe8b8e53 --- /dev/null +++ b/iphone/Maps/Classes/MWMOpeningHours.mm @@ -0,0 +1,128 @@ +#import "MWMOpeningHours.h" +#import "MWMOpeningHoursCommon.h" + + +#include "3party/opening_hours/opening_hours.hpp" +#include "editor/opening_hours_ui.hpp" +#include "editor/ui2oh.hpp" + +using namespace editor; +using namespace osmoh; + +namespace +{ +NSString * stringFromTimeSpan(Timespan const & timeSpan) +{ + return [NSString stringWithFormat:@"%@ - %@", stringFromTime(timeSpan.GetStart()), + stringFromTime(timeSpan.GetEnd())]; +} + +NSString * breaksFromClosedTime(TTimespans const & closedTimes) +{ + NSMutableString * breaks = [@"" mutableCopy]; + for (auto & ct : closedTimes) + { + [breaks appendString:@"\n"]; + [breaks appendString:stringFromTimeSpan(ct)]; + } + return breaks.copy; +} + +void addToday(ui::TimeTable const & tt, vector & allDays) +{ + NSString * workingDays; + NSString * workingTimes; + NSString * breaks; + + if (tt.IsTwentyFourHours()) + { + workingDays = L(@"twentyfour_seven"); + workingTimes = @""; + breaks = @""; + } + else + { + BOOL const everyDay = (tt.GetOpeningDays().size() == 7); + workingDays = everyDay ? L(@"daily") : L(@"today"); + workingTimes = stringFromTimeSpan(tt.GetOpeningTime()); + breaks = breaksFromClosedTime(tt.GetExcludeTime()); + } + + allDays.emplace(allDays.begin(), workingDays, workingTimes, breaks); +} + +void addClosedToday(vector & allDays) +{ + allDays.emplace(allDays.begin(), L(@"day_off_today")); +} + +void addDay(ui::TimeTable const & tt, vector & allDays) +{ + NSString * workingDays = stringFromOpeningDays(tt.GetOpeningDays()); + NSString * workingTimes; + NSString * breaks; + if (tt.IsTwentyFourHours()) + { + workingTimes = L(@"twentyfour_seven"); + } + else + { + workingTimes = stringFromTimeSpan(tt.GetOpeningTime()); + breaks = breaksFromClosedTime(tt.GetExcludeTime()); + } + allDays.emplace_back(workingDays, workingTimes, breaks); +} + +void addUnhandledDays(ui::TOpeningDays const & days, vector & allDays) +{ + if (days.empty()) + return; + + allDays.emplace_back(stringFromOpeningDays(days)); +} + +} // namespace + +@implementation MWMOpeningHours + ++ (vector)processRawString:(NSString *)str +{ + ui::TimeTableSet timeTableSet; + osmoh::OpeningHours oh(str.UTF8String); + if (!MakeTimeTableSet(oh, timeTableSet)) + return {}; + + vector days; + + NSCalendar * cal = [NSCalendar currentCalendar]; + cal.locale = [NSLocale currentLocale]; + + auto const timeTablesSize = timeTableSet.Size(); + auto const today = static_cast([cal components:NSCalendarUnitWeekday fromDate:[NSDate date]].weekday); + auto const unhandledDays = timeTableSet.GetUnhandledDays(); + + /// Schedule contains more than one rule for all days or unhandled days. + BOOL const isExtendedSchedule = timeTablesSize != 1 || !unhandledDays.empty(); + BOOL hasCurrentDay = NO; + + for (auto const & tt : timeTableSet) + { + ui::TOpeningDays const & workingDays = tt.GetOpeningDays(); + if (workingDays.find(today) != workingDays.end()) + { + hasCurrentDay = YES; + addToday(tt, days); + } + + if (isExtendedSchedule) + addDay(tt, days); + } + + if (!hasCurrentDay) + addClosedToday(days); + + addUnhandledDays(unhandledDays, days); + return days; +} + +@end diff --git a/iphone/Maps/Classes/MWMOpeningHoursCell.h b/iphone/Maps/Classes/MWMOpeningHoursCell.h new file mode 100644 index 0000000000..211cf927a9 --- /dev/null +++ b/iphone/Maps/Classes/MWMOpeningHoursCell.h @@ -0,0 +1,10 @@ +#import "MWMTableViewCell.h" + +@protocol MWMPlacePageCellUpdateProtocol; + +@interface MWMOpeningHoursCell : MWMTableViewCell + +- (void)configureWithOpeningHours:(NSString *)openningHours + updateLayoutDelegate:(id)delegate + isClosedNow:(BOOL)isClosedNow; +@end diff --git a/iphone/Maps/Classes/MWMOpeningHoursCell.mm b/iphone/Maps/Classes/MWMOpeningHoursCell.mm new file mode 100644 index 0000000000..850eaac266 --- /dev/null +++ b/iphone/Maps/Classes/MWMOpeningHoursCell.mm @@ -0,0 +1,200 @@ +#import "Common.h" +#import "MWMOpeningHours.h" +#import "MWMOpeningHoursCell.h" +#import "MWMPlacePageCellUpdateProtocol.h" + +#include "std/array.hpp" + +namespace +{ +array kOHClasses = {{@"_MWMOHHeaderCell", @"_MWMOHSubCell"}}; +void * kContext = &kContext; +NSString * const kTableViewContentSizeKeyPath = @"contentSize"; + +} // namespace + +#pragma mark - _MWMOHHeaderCell + +@interface _MWMOHHeaderCell : MWMTableViewCell + +@property(weak, nonatomic) IBOutlet UILabel * today; +@property(weak, nonatomic) IBOutlet UILabel * breaks; +@property(weak, nonatomic) IBOutlet UILabel * closedNow; +@property(weak, nonatomic) IBOutlet UIImageView * arrowIcon; +@property(weak, nonatomic) IBOutlet UIImageView * separator; + +@property(copy, nonatomic) TMWMVoidBlock tapAction; + +@end + +@implementation _MWMOHHeaderCell + +- (IBAction)extendTap +{ + if (!self.tapAction) + return; + + self.tapAction(); + [UIView animateWithDuration:kDefaultAnimationDuration animations:^ + { + self.arrowIcon.transform = CGAffineTransformIsIdentity(self.arrowIcon.transform) ? + CGAffineTransformMakeRotation(M_PI) : + CGAffineTransformIdentity; + }]; +} + +@end + +#pragma mark - _MWMOHSubCell + +@interface _MWMOHSubCell :MWMTableViewCell + +@property(weak, nonatomic) IBOutlet UILabel * days; +@property(weak, nonatomic) IBOutlet UILabel * schedule; +@property(weak, nonatomic) IBOutlet UILabel * breaks; +@property(weak, nonatomic) IBOutlet UIImageView * separator; + +@end + +@implementation _MWMOHSubCell + +@end + +@interface MWMOpeningHoursCell () + +@property(weak, nonatomic) IBOutlet UITableView * tableView; +@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewHeight; + +@property(weak, nonatomic) id delegate; + +@property(nonatomic) BOOL isExtended; +@property(nonatomic) BOOL isClosedNow; + +@end + +@implementation MWMOpeningHoursCell +{ + vector m_days; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + for (auto s : kOHClasses) + [self.tableView registerNib:[UINib nibWithNibName:s bundle:nil] forCellReuseIdentifier:s]; + + self.tableView.estimatedRowHeight = 48; + self.tableView.rowHeight = UITableViewAutomaticDimension; + [self registerObserver]; +} + +- (void)configureWithOpeningHours:(NSString *)openningHours + updateLayoutDelegate:(id)delegate + isClosedNow:(BOOL)isClosedNow; +{ + self.tableView.delegate = nil; + self.tableView.dataSource = nil; + self.delegate = delegate; + self.isClosedNow = isClosedNow; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ + { + self->m_days = [MWMOpeningHours processRawString:openningHours]; + dispatch_async(dispatch_get_main_queue(), ^ + { + self.tableView.delegate = self; + self.tableView.dataSource = self; + [self.tableView reloadData]; + }); + }); +} + +#pragma mark - UITableView + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.isExtended ? m_days.size() : 1; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + auto const & day = m_days[indexPath.row]; + BOOL const isSeparatorHidden = self.isExtended ? indexPath.row == 0 : indexPath.row == m_days.size() - 1; + if (indexPath.row == 0) + { + _MWMOHHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:[_MWMOHHeaderCell className]]; + cell.today.text = day.TodayTime(); + cell.breaks.text = day.m_breaks; + cell.closedNow.text = self.isClosedNow ? L(@"closed_now") : nil;; + if (m_days.size() > 1) + { + cell.tapAction = ^ + { + self.isExtended = !self.isExtended; + + NSMutableArray * ip = [@[] mutableCopy]; + + for (auto i = 1; i < self->m_days.size(); i++) + [ip addObject:[NSIndexPath indexPathForRow:i inSection:0]]; + + if (self.isExtended) + [self.tableView insertRowsAtIndexPaths:ip withRowAnimation:UITableViewRowAnimationAutomatic]; + else + [self.tableView deleteRowsAtIndexPaths:ip withRowAnimation:UITableViewRowAnimationAutomatic]; + }; + cell.arrowIcon.hidden = NO; + } + else + { + cell.tapAction = nil; + cell.arrowIcon.hidden = YES; + } + cell.separator.hidden = isSeparatorHidden; + return cell; + } + else + { + _MWMOHSubCell * cell = [tableView dequeueReusableCellWithIdentifier:[_MWMOHSubCell className]]; + cell.days.text = day.m_workingDays; + cell.schedule.text = day.m_workingTimes ? day.m_workingTimes : L(@"closed"); + cell.breaks.text = day.m_breaks; + cell.separator.hidden = isSeparatorHidden; + return cell; + } +} + +#pragma mark - Observer's methods + +- (void)dealloc +{ + [self unregisterObserver]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == kContext) + { + NSValue * s = change[@"new"]; + CGFloat const height = s.CGSizeValue.height; + self.tableViewHeight.constant = height; + [self setNeedsLayout]; + [self.delegate updateCellWithForceReposition:NO]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (void)unregisterObserver +{ + [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext]; +} + +- (void)registerObserver +{ + [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext]; +} +@end diff --git a/iphone/Maps/Classes/MWMPPView.h b/iphone/Maps/Classes/MWMPPView.h new file mode 100644 index 0000000000..60049b831b --- /dev/null +++ b/iphone/Maps/Classes/MWMPPView.h @@ -0,0 +1,31 @@ +#pragma mark - MWMPPScrollView + +@protocol MWMPlacePageViewUpdateProtocol + +- (void)updateWithHeight:(CGFloat)height; + +@end + +@interface MWMPPScrollView : UIScrollView + +- (instancetype)initWithFrame:(CGRect)frame inactiveView:(UIView *)inactiveView; + +@property(weak, nonatomic) UIView * inactiveView; + +@end + +#pragma mark - MWMPPView + +@interface MWMPPView : UIView + +@property(weak, nonatomic) IBOutlet UIImageView * top; +@property(weak, nonatomic) IBOutlet UIImageView * spinner; +@property(weak, nonatomic) IBOutlet UITableView * tableView; + +@property(nonatomic) CGFloat currentContentHeight; +@property(nonatomic) id delegate; + +- (void)hideTableView:(BOOL)isHidden; + +@end + diff --git a/iphone/Maps/Classes/MWMPPView.mm b/iphone/Maps/Classes/MWMPPView.mm new file mode 100644 index 0000000000..d6c80dd84a --- /dev/null +++ b/iphone/Maps/Classes/MWMPPView.mm @@ -0,0 +1,101 @@ +#import "MWMPPView.h" +#import "UIColor+MapsMeColor.h" + +namespace +{ +void * kContext = &kContext; +NSString * const kTableViewContentSizeKeyPath = @"contentSize"; + +} // namespace + +#pragma mark - MWMPPScrollView + +@implementation MWMPPScrollView + +- (instancetype)initWithFrame:(CGRect)frame inactiveView:(UIView *)inactiveView +{ + self = [super initWithFrame:frame]; + if (self) + _inactiveView = inactiveView; + return self; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + if (point.y > [self convertRect:self.inactiveView.bounds fromView:self.inactiveView].origin.y) + return YES; + return NO; +} + +@end + +#pragma mark - MWMPPView + +@implementation MWMPPView + +- (void)hideTableView:(BOOL)isHidden +{ + if (isHidden) + { + self.tableView.alpha = 0.; + self.spinner.hidden = NO; + [self.spinner startAnimating]; + } + else + { + self.tableView.alpha = 1.; + self.spinner.hidden = YES; + [self.spinner stopAnimating]; + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == kContext) + { + NSValue * s = change[@"new"]; + CGFloat const height = s.CGSizeValue.height; + if (fabs(height - self.currentContentHeight) > 0.5) + { + self.currentContentHeight = height; + self.height = height + self.top.height; + [self setNeedsLayout]; + [self.delegate updateWithHeight:self.height]; + } + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext]; + + self.tableView.estimatedRowHeight = 44.; + self.tableView.rowHeight = UITableViewAutomaticDimension; + + self.tableView.contentInset = {.top = -36}; + + NSUInteger const animationImagesCount = 12; + NSMutableArray * animationImages = [NSMutableArray arrayWithCapacity:animationImagesCount]; + NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light"; + for (NSUInteger i = 0; i < animationImagesCount; ++i) + animationImages[i] = [UIImage imageNamed:[NSString stringWithFormat:@"Spinner_%@_%@", @(i+1), postfix]]; + + self.spinner.animationDuration = 0.8; + self.spinner.animationImages = animationImages; + [self.spinner startAnimating]; +} + +- (void)dealloc +{ + [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext]; +} + +@end + diff --git a/iphone/Maps/Classes/MWMPlacePage.mm b/iphone/Maps/Classes/MWMPlacePage.mm index baa1304972..9d411d5e9e 100644 --- a/iphone/Maps/Classes/MWMPlacePage.mm +++ b/iphone/Maps/Classes/MWMPlacePage.mm @@ -10,7 +10,7 @@ static NSString * const kPlacePageNibIdentifier = @"PlacePageView"; static NSString * const kPlacePageViewCenterKeyPath = @"center"; -extern NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; +static NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; extern NSString * const kPP2BookmarkEditingIPADSegue = @"PP2BookmarkEditingIPAD"; @interface MWMPlacePage () diff --git a/iphone/Maps/Classes/MWMPlacePageActionBar.h b/iphone/Maps/Classes/MWMPlacePageActionBar.h index 0e09565350..1ccd7465c5 100644 --- a/iphone/Maps/Classes/MWMPlacePageActionBar.h +++ b/iphone/Maps/Classes/MWMPlacePageActionBar.h @@ -1,7 +1,25 @@ @class MWMPlacePageViewManager; +@class MWMPlacePageData; + +@protocol MWMActionBarSharedData + +- (BOOL)isBookmark; +- (BOOL)isBooking; +- (BOOL)isApi; +- (BOOL)isMyPosition; +- (NSString *)title; +- (NSString *)subtitle; +- (NSString *)phoneNumber; + +@end + +@protocol MWMActionBarProtocol; @interface MWMPlacePageActionBar : SolidTouchView ++ (MWMPlacePageActionBar *)actionBarWithDelegate:(id)delegate; +- (void)configureWithData:(id)data; + @property (nonatomic) BOOL isBookmark; - (UIView *)shareAnchor; diff --git a/iphone/Maps/Classes/MWMPlacePageActionBar.mm b/iphone/Maps/Classes/MWMPlacePageActionBar.mm index 5a85e0a7c3..2701045a79 100644 --- a/iphone/Maps/Classes/MWMPlacePageActionBar.mm +++ b/iphone/Maps/Classes/MWMPlacePageActionBar.mm @@ -3,6 +3,7 @@ #import "MWMActionBarButton.h" #import "MWMBasePlacePageView.h" #import "MWMPlacePageEntity.h" +#import "MWMPlacePageProtocol.h" #import "MWMPlacePageViewManager.h" #import "MapViewController.h" #import "MapsAppDelegate.h" @@ -13,12 +14,6 @@ extern NSString * const kAlohalyticsTapEventKey; -namespace -{ -NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; - -} // namespace - @interface MWMPlacePageActionBar () { vector m_visibleButtons; @@ -30,14 +25,34 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; @property(weak, nonatomic) IBOutlet UIImageView * separator; @property(nonatomic) BOOL isPrepareRouteMode; +@property(weak, nonatomic) id data; +@property(weak, nonatomic) id delegate; + @end @implementation MWMPlacePageActionBar ++ (MWMPlacePageActionBar *)actionBarWithDelegate:(id)delegate +{ + MWMPlacePageActionBar * bar = [[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil] + firstObject]; + bar.delegate = delegate; + return bar; +} + +- (void)configureWithData:(id)data +{ + self.data = data; + self.isPrepareRouteMode = MapsAppDelegate.theApp.routingPlaneMode != MWMRoutingPlaneModeNone; + self.isBookmark = data.isBookmark; + [self configureButtons]; + self.autoresizingMask = UIViewAutoresizingNone; +} + + (MWMPlacePageActionBar *)actionBarForPlacePageManager:(MWMPlacePageViewManager *)placePageManager { MWMPlacePageActionBar * bar = - [[NSBundle.mainBundle loadNibNamed:kPlacePageActionBarNibName owner:nil options:nil] + [[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil] firstObject]; [bar configureWithPlacePageManager:placePageManager]; return bar; @@ -56,16 +71,16 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; { m_visibleButtons.clear(); m_additionalButtons.clear(); - MWMPlacePageEntity * entity = self.placePageManager.entity; - NSString * phone = [entity getCellValue:MWMPlacePageCellTypePhoneNumber]; + auto data = static_cast>(isIOS7 ? self.placePageManager.entity : self.data); + NSString * phone = data.phoneNumber; BOOL const isIphone = [[UIDevice currentDevice].model isEqualToString:@"iPhone"]; BOOL const isPhoneNotEmpty = phone.length > 0; - BOOL const isBooking = entity.isBooking; + BOOL const isBooking = data.isBooking; BOOL const itHasPhoneNumber = isIphone && isPhoneNotEmpty; - BOOL const isApi = entity.isApi; + BOOL const isApi = data.isApi; BOOL const isP2P = self.isPrepareRouteMode; - BOOL const isMyPosition = entity.isMyPosition; + BOOL const isMyPosition = data.isMyPosition; if (isMyPosition) { @@ -171,21 +186,24 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; - (void)tapOnButtonWithType:(EButton)type { - MWMPlacePageViewManager * placePageManager = self.placePageManager; + id delegate = IPAD ? self.placePageManager : self.delegate; + switch (type) { - case EButton::Api: [placePageManager apiBack]; break; - case EButton::Booking: [placePageManager book:NO]; break; - case EButton::Call: [placePageManager call]; break; + case EButton::Api: [delegate apiBack]; break; + case EButton::Booking: [delegate book:NO]; break; + case EButton::Call: [delegate call]; break; case EButton::Bookmark: if (self.isBookmark) - [placePageManager removeBookmark]; + [delegate removeBookmark]; else - [placePageManager addBookmark]; + [delegate addBookmark]; + + self.isBookmark = !self.isBookmark; break; - case EButton::RouteFrom: [placePageManager routeFrom]; break; - case EButton::RouteTo: [placePageManager routeTo]; break; - case EButton::Share: [placePageManager share]; break; + case EButton::RouteFrom: [delegate routeFrom]; break; + case EButton::RouteTo: [delegate routeTo]; break; + case EButton::Share: [delegate share]; break; case EButton::More: [self showActionSheet]; break; case EButton::Spacer: break; } @@ -196,16 +214,16 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; - (void)showActionSheet { NSString * cancel = L(@"cancel"); - MWMPlacePageEntity * entity = self.placePageManager.entity; - BOOL const isTitleNotEmpty = entity.title.length > 0; - NSString * title = isTitleNotEmpty ? entity.title : entity.subtitle; - NSString * subtitle = isTitleNotEmpty ? entity.subtitle : nil; + auto data = static_cast>(IPAD ? self.placePageManager.entity : self.data); + BOOL const isTitleNotEmpty = data.title.length > 0; + NSString * title = isTitleNotEmpty ? data.title : data.subtitle; + NSString * subtitle = isTitleNotEmpty ? data.subtitle : nil; UIViewController * vc = static_cast([MapViewController controller]); NSMutableArray * titles = [@[] mutableCopy]; for (auto const buttonType : m_additionalButtons) { - BOOL const isSelected = buttonType == EButton::Bookmark ? self.isBookmark : NO; + BOOL const isSelected = buttonType == EButton::Bookmark ? data.isBookmark : NO; if (NSString * title = titleForButton(buttonType, isSelected)) [titles addObject:title]; else @@ -246,6 +264,10 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; - (void)layoutSubviews { [super layoutSubviews]; + self.width = self.superview.width; + if (IPAD) + self.origin = {0, self.superview.height - 48}; + self.separator.width = self.width; CGFloat const buttonWidth = self.width / self.buttons.count; for (UIView * button in self.buttons) @@ -255,6 +277,11 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar"; } } +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; +} + #pragma mark - Properties - (void)setIsBookmark:(BOOL)isBookmark diff --git a/iphone/Maps/Classes/MWMPlacePageActionBar.xib b/iphone/Maps/Classes/MWMPlacePageActionBar.xib new file mode 100644 index 0000000000..077efe79ec --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageActionBar.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h index 3424bf4856..ac32f2671f 100644 --- a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h +++ b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h @@ -1,13 +1,6 @@ #import "MWMTableViewCell.h" -@protocol MWMPlacePageBookmarkDelegate - -- (void)reloadBookmark; -- (void)editBookmarkTap; -- (void)moreTap; - -@end - +@protocol MWMPlacePageBookmarkDelegate; @class MWMPlacePage; @interface MWMPlacePageBookmarkCell : MWMTableViewCell diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm index 842528fbee..024d8c303c 100644 --- a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm +++ b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm @@ -1,4 +1,5 @@ #import "MWMPlacePageBookmarkCell.h" +#import "MWMPlacePageBookmarkDelegate.h" #import "Common.h" #import "MapViewController.h" #import "Statistics.h" diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h b/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h new file mode 100644 index 0000000000..e0e7202e92 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h @@ -0,0 +1,8 @@ +@protocol MWMPlacePageBookmarkDelegate + +@required +- (void)editBookmarkTap; +- (void)reloadBookmark; +- (void)moreTap; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageButtonCell.h b/iphone/Maps/Classes/MWMPlacePageButtonCell.h index e8007704d8..3c6c282b80 100644 --- a/iphone/Maps/Classes/MWMPlacePageButtonCell.h +++ b/iphone/Maps/Classes/MWMPlacePageButtonCell.h @@ -1,10 +1,19 @@ #import "MWMTableViewCell.h" #import "MWMPlacePageEntity.h" +#import "MWMPlacePageData.h" @class MWMPlacePageViewManager; +@protocol MWMPlacePageButtonsProtocol; + @interface MWMPlacePageButtonCell : MWMTableViewCell -- (void)config:(MWMPlacePageViewManager *)manager forType:(MWMPlacePageCellType)type; +- (void)config:(MWMPlacePageViewManager *)manager forType:(MWMPlacePageCellType)type NS_DEPRECATED_IOS(7_0, 8_0); + +- (void)configForRow:(place_page::ButtonsRows)row + withDelegate:(id)delegate NS_AVAILABLE_IOS(8_0); + +- (void)setEnabled:(BOOL)enabled NS_AVAILABLE_IOS(8_0); +- (BOOL)isEnabled NS_AVAILABLE_IOS(8_0); @end diff --git a/iphone/Maps/Classes/MWMPlacePageButtonCell.mm b/iphone/Maps/Classes/MWMPlacePageButtonCell.mm index 5b0f81416a..203bc3bfbd 100644 --- a/iphone/Maps/Classes/MWMPlacePageButtonCell.mm +++ b/iphone/Maps/Classes/MWMPlacePageButtonCell.mm @@ -1,7 +1,9 @@ +#import "Common.h" #import "MWMPlacePageButtonCell.h" #import "MWMFrameworkListener.h" #import "MWMPlacePageViewManager.h" #import "UIColor+MapsMeColor.h" +#import "MWMPlacePageProtocol.h" @interface MWMPlacePageButtonCell () @@ -10,6 +12,8 @@ @property(nonatomic) MWMPlacePageCellType type; @property(nonatomic) storage::TCountryId countryId; +@property(weak, nonatomic) id delegate; +@property(nonatomic) place_page::ButtonsRows rowType; @end @implementation MWMPlacePageButtonCell @@ -22,16 +26,59 @@ [self refreshButtonEnabledState]; } +- (void)setEnabled:(BOOL)enabled +{ + self.titleButton.enabled = enabled; +} + +- (BOOL)isEnabled +{ + return self.titleButton.isEnabled; +} + +- (void)configForRow:(place_page::ButtonsRows)row withDelegate:(id)delegate +{ + self.delegate = delegate; + self.rowType = row; + switch(row) + { + case place_page::ButtonsRows::AddPlace: + [self.titleButton setTitle:L(@"placepage_add_place_button") forState:UIControlStateNormal]; + break; + case place_page::ButtonsRows::EditPlace: + [self.titleButton setTitle:L(@"edit_place") forState:UIControlStateNormal]; + break; + case place_page::ButtonsRows::AddBusiness: + [self.titleButton setTitle:L(@"placepage_add_business_button") forState:UIControlStateNormal]; + break; + case place_page::ButtonsRows::HotelDescription: + [self.titleButton setTitle:L(@"details") forState:UIControlStateNormal]; + break; + } +} + - (IBAction)buttonTap { - MWMPlacePageViewManager * manager = self.manager; - switch (self.type) + if (IPAD) + { + switch (self.type) + { + case MWMPlacePageCellTypeEditButton: [self.manager editPlace]; break; + case MWMPlacePageCellTypeAddBusinessButton: [self.manager addBusiness]; break; + case MWMPlacePageCellTypeAddPlaceButton: [self.manager addPlace]; break; + case MWMPlacePageCellTypeBookingMore: [self.manager book:YES]; break; + default: NSAssert(false, @"Incorrect cell type!"); break; + } + return; + } + + using namespace place_page; + switch (self.rowType) { - case MWMPlacePageCellTypeEditButton: [manager editPlace]; break; - case MWMPlacePageCellTypeAddBusinessButton: [manager addBusiness]; break; - case MWMPlacePageCellTypeAddPlaceButton: [manager addPlace]; break; - case MWMPlacePageCellTypeBookingMore: [manager book:YES]; break; - default: NSAssert(false, @"Incorrect cell type!"); break; + case ButtonsRows::AddPlace: [self.delegate addPlace]; break; + case ButtonsRows::EditPlace: [self.delegate editPlace]; break; + case ButtonsRows::AddBusiness: [self.delegate addBusiness]; break; + case ButtonsRows::HotelDescription: [self.delegate book:YES]; break; } } diff --git a/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h b/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h new file mode 100644 index 0000000000..3b4bcfe7c1 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h @@ -0,0 +1,5 @@ +@protocol MWMPlacePageCellUpdateProtocol + +- (void)updateCellWithForceReposition:(BOOL)isForceReposition; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageData.h b/iphone/Maps/Classes/MWMPlacePageData.h new file mode 100644 index 0000000000..4496f1df43 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageData.h @@ -0,0 +1,103 @@ +#include "map/place_page_info.hpp" + +#include "std/vector.hpp" + +namespace place_page +{ +enum class Sections +{ + Preview, + Bookmark, + Metainfo, + Buttons +}; + +enum class MetainfoRows +{ + OpeningHours, + Phone, + Address, + Website, + Email, + Cuisine, + Operator, + Internet, + Coordinate +}; + +enum class ButtonsRows +{ + AddBusiness, + EditPlace, + AddPlace, + HotelDescription +}; + +enum class OpeningHours +{ + AllDay, + Open, + Closed, + Unknown +}; + +} // namespace place_page_data + +/// ViewModel for place page. +@interface MWMPlacePageData : NSObject + +// ready callback will be called from main queue. +- (instancetype)initWithPlacePageInfo:(place_page::Info const &)info; + +- (void)updateBookmarkStatus:(BOOL)isBookmark; + +/// Country id for changing place page's fields which depend on application state. +- (storage::TCountryId const &)countryId; +- (FeatureID const &)featureId; + +// Regular +- (NSString *)title; +- (NSString *)subtitle; +- (place_page::OpeningHours)schedule; +- (NSString *)address; + +// Booking +- (NSString *)bookingRating; +- (NSString *)bookingApproximatePricing; +- (NSURL *)bookingURL; +- (NSURL *)bookingDescriptionURL; +- (NSString *)hotelId; +- (void)assignOnlinePriceToLabel:(UILabel *)label; + +// API +- (NSString *)apiURL; + +// Bookmark +- (NSString *)externalTitle; +- (NSString *)bookmarkColor; +- (NSString *)bookmarkDescription; +- (NSString *)bookmarkCategory; +- (BookmarkAndCategory)bac; + +// Table view's data +- (vector const &)sections; +- (vector const &)metainfoRows; +- (vector const &)buttonsRows; + +// Table view metainfo rows +- (NSString *)stringForRow:(place_page::MetainfoRows)row; + +// Helpers +- (NSString *)phoneNumber; +- (BOOL)isBookmark; +- (BOOL)isApi; +- (BOOL)isBooking; +- (BOOL)isHTMLDescription; +- (BOOL)isMyPosition; + +// Coordinates +- (m2::PointD const &)mercator; +- (ms::LatLon)latLon; ++ (void)toggleCoordinateSystem; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageData.mm b/iphone/Maps/Classes/MWMPlacePageData.mm new file mode 100644 index 0000000000..15c934909b --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageData.mm @@ -0,0 +1,406 @@ +#import "MWMPlacePageData.h" + +#include "Framework.h" + +#include "base/string_utils.hpp" + +#include "3party/opening_hours/opening_hours.hpp" + +namespace +{ +NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; + +} // namespace + +using namespace place_page; + +@implementation MWMPlacePageData +{ + Info m_info; + + vector m_sections; + vector m_metainfoRows; + vector m_buttonsRows; +} + +- (instancetype)initWithPlacePageInfo:(Info const &)info +{ + self = [super init]; + if (self) + { + m_info = info; + [self fillSections]; + } + + return self; +} + +- (void)fillSections +{ + m_sections.clear(); + m_metainfoRows.clear(); + m_buttonsRows.clear(); + + m_sections.push_back(Sections::Preview); + + // It's bookmark. + if (m_info.IsBookmark()) + m_sections.push_back(Sections::Bookmark); + + // There is always at least coordinate meta field. + m_sections.push_back(Sections::Metainfo); + [self fillMetaInfoSection]; + + // There is at least one of these buttons. + if (m_info.ShouldShowAddPlace() || m_info.ShouldShowEditPlace() || m_info.ShouldShowAddBusiness() || m_info.IsSponsoredHotel()) + { + m_sections.push_back(Sections::Buttons); + [self fillButtonsSection]; + } +} + +- (void)fillMetaInfoSection +{ + using namespace osm; + auto const availableProperties = m_info.AvailableProperties(); + // We can't match each metadata property to its UI field and thats why we need to use our own enum. + for (auto const p : availableProperties) + { + switch (p) + { + case Props::OpeningHours: + m_metainfoRows.push_back(MetainfoRows::OpeningHours); + break; + case Props::Phone: + m_metainfoRows.push_back(MetainfoRows::Phone); + break; + case Props::Website: + m_metainfoRows.push_back(MetainfoRows::Website); + break; + case Props::Email: + m_metainfoRows.push_back(MetainfoRows::Email); + break; + case Props::Cuisine: + m_metainfoRows.push_back(MetainfoRows::Cuisine); + break; + case Props::Operator: + m_metainfoRows.push_back(MetainfoRows::Operator); + break; + case Props::Internet: + m_metainfoRows.push_back(MetainfoRows::Internet); + break; + + case Props::Wikipedia: + case Props::Elevation: + case Props::Stars: + case Props::Flats: + case Props::BuildingLevels: + case Props::Fax: + break; + } + } + + auto const address = m_info.GetAddress(); + if (!address.empty()) + m_metainfoRows.push_back(MetainfoRows::Address); + + m_metainfoRows.push_back(MetainfoRows::Coordinate); +} + +- (void)fillButtonsSection +{ + // We don't have to show edit, add place or business if it's booking object. + if (m_info.IsSponsoredHotel()) + { + m_buttonsRows.push_back(ButtonsRows::HotelDescription); + return; + } + + if (m_info.ShouldShowAddPlace()) + m_buttonsRows.push_back(ButtonsRows::AddPlace); + + if (m_info.ShouldShowEditPlace()) + m_buttonsRows.push_back(ButtonsRows::EditPlace); + + if (m_info.ShouldShowAddBusiness()) + m_buttonsRows.push_back(ButtonsRows::AddBusiness); +} + +- (void)updateBookmarkStatus:(BOOL)isBookmark +{ + auto & f = GetFramework(); + auto & bmManager = f.GetBookmarkManager(); + if (isBookmark) + { + auto const categoryIndex = static_cast(f.LastEditedBMCategory()); + BookmarkData bmData{m_info.FormatNewBookmarkName(), f.LastEditedBMType()}; + auto const bookmarkIndex = + static_cast(bmManager.AddBookmark(categoryIndex,self.mercator, bmData)); + + auto category = f.GetBmCategory(categoryIndex); + NSAssert(category, @"Category can't be nullptr!"); + { + BookmarkCategory::Guard guard(*category); + Bookmark * bookmark = + static_cast(guard.m_controller.GetUserMarkForEdit(bookmarkIndex)); + f.FillBookmarkInfo(*bookmark, {bookmarkIndex, categoryIndex}, m_info); + } + m_sections.insert(m_sections.begin() + 1, Sections::Bookmark); + } + else + { + auto const bac = m_info.GetBookmarkAndCategory(); + auto category = bmManager.GetBmCategory(bac.m_categoryIndex); + NSAssert(category, @"Category can't be nullptr!"); + { + BookmarkCategory::Guard guard(*category); + guard.m_controller.DeleteUserMark(bac.m_bookmarkIndex); + } + category->SaveToKMLFile(); + + m_info.m_bac = {}; + m_sections.erase(remove(m_sections.begin(), m_sections.end(), Sections::Bookmark)); + } +} + +#pragma mark - Getters + +- (storage::TCountryId const &)countryId +{ + return m_info.m_countryId; +} + +- (FeatureID const &)featureId +{ + return m_info.GetID(); +} + +- (NSString *)title +{ + return @(m_info.GetTitle().c_str()); +} + +- (NSString *)subtitle +{ + return @(m_info.GetSubtitle().c_str()); +} + +- (place_page::OpeningHours)schedule; +{ + auto const raw = m_info.GetOpeningHours(); + if (raw.empty()) + return place_page::OpeningHours::Unknown; + + auto const t = time(nullptr); + osmoh::OpeningHours oh(raw); + if (oh.IsValid()) + { + + if (oh.IsTwentyFourHours()) + return place_page::OpeningHours::AllDay; + else if (oh.IsOpen(t)) + return place_page::OpeningHours::Open; + else if (oh.IsClosed(t)) + return place_page::OpeningHours::Closed; + else + return place_page::OpeningHours::Unknown; + } + else + { + return place_page::OpeningHours::Unknown; + } +} + +- (NSString *)bookingRating +{ + return m_info.IsHotel() ? @(m_info.GetRatingFormatted().c_str()) : nil; +} + +- (NSString *)bookingApproximatePricing +{ + return m_info.IsHotel() ? @(m_info.GetApproximatePricing().c_str()) : nil; +} + +- (NSURL *)bookingURL +{ + return m_info.IsSponsoredHotel() ? [NSURL URLWithString:@(m_info.m_sponsoredBookingUrl.c_str())] : nil; +} + +- (NSURL *)bookingDescriptionURL +{ + return m_info.IsSponsoredHotel() ? [NSURL URLWithString:@(m_info.m_sponsoredDescriptionUrl.c_str())] : nil; +} + +- (NSString *)hotelId +{ + return m_info.IsSponsoredHotel() ? @(m_info.GetMetadata().Get(feature::Metadata::FMD_SPONSORED_ID).c_str()) + : nil; +} + +- (void)assignOnlinePriceToLabel:(UILabel *)label +{ + NSAssert(m_info.IsSponsoredHotel(), @"Online price must be assigned to booking object!"); + if (Platform::ConnectionStatus() == Platform::EConnectionType::CONNECTION_NONE) + return; + + NSNumberFormatter * currencyFormatter = [[NSNumberFormatter alloc] init]; + if (currencyFormatter.currencyCode.length != 3) + currencyFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + + currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + currencyFormatter.maximumFractionDigits = 0; + + string const currency = currencyFormatter.currencyCode.UTF8String; + auto & api = GetFramework().GetBookingApi(); + + auto const func = [self, label, currency, currencyFormatter](string const & minPrice, string const & priceCurrency) + { + if (currency != priceCurrency) + return; + + NSNumberFormatter * decimalFormatter = [[NSNumberFormatter alloc] init]; + decimalFormatter.numberStyle = NSNumberFormatterDecimalStyle; + + NSString * currencyString = [currencyFormatter stringFromNumber:[decimalFormatter numberFromString: + [@(minPrice.c_str()) stringByReplacingOccurrencesOfString:@"." + withString:decimalFormatter.decimalSeparator]]]; + + NSString * pattern = [L(@"place_page_starting_from") stringByReplacingOccurrencesOfString:@"%s" withString:@"%@"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + label.text = [NSString stringWithFormat:pattern, currencyString]; + }); + }; + + api.GetMinPrice(self.hotelId.UTF8String, currency, func); +} + +- (NSString *)address +{ + return @(m_info.GetAddress().c_str()); +} + +- (NSString *)apiURL +{ + return @(m_info.GetApiUrl().c_str()); +} + +- (NSString *)externalTitle +{ + return m_info.IsBookmark() && m_info.m_bookmarkTitle != m_info.GetTitle() ? + @(m_info.m_bookmarkTitle.c_str()) + : nil; +} + +- (NSString *)bookmarkColor +{ + return m_info.IsBookmark() ? @(m_info.m_bookmarkColorName.c_str()) : nil;; +} + +- (NSString *)bookmarkDescription +{ + return m_info.IsBookmark() ? @(m_info.m_bookmarkDescription.c_str()) : nil; +} + +- (NSString *)bookmarkCategory +{ + return m_info.IsBookmark() ? @(m_info.m_bookmarkCategoryName.c_str()) : nil;; +} + +- (BookmarkAndCategory)bac; +{ + return m_info.IsBookmark() ? m_info.m_bac : BookmarkAndCategory(); +} + +- (vector const &)sections +{ + return m_sections; +} + +- (vector const &)metainfoRows +{ + return m_metainfoRows; +} + +- (vector const &)buttonsRows +{ + return m_buttonsRows; +} + +- (NSString *)stringForRow:(MetainfoRows)row +{ + switch (row) + { + case MetainfoRows::OpeningHours: + return @(m_info.GetOpeningHours().c_str()); + case MetainfoRows::Phone: + return @(m_info.GetPhone().c_str()); + case MetainfoRows::Address: + return @(m_info.GetAddress().c_str()); + case MetainfoRows::Website: + return @(m_info.GetWebsite().c_str()); + case MetainfoRows::Email: + return @(m_info.GetEmail().c_str()); + case MetainfoRows::Cuisine: + return @(strings::JoinStrings(m_info.GetCuisines(), Info::kSubtitleSeparator).c_str()); + case MetainfoRows::Operator: + return @(m_info.GetOperator().c_str()); + case MetainfoRows::Internet: + return L(@"WiFi_available"); + case MetainfoRows::Coordinate: + return @(m_info.GetFormattedCoordinate([[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsLatLonAsDMSKey]).c_str()); + } +} + +#pragma mark - Helpres + +- (NSString *)phoneNumber +{ + return @(m_info.GetPhone().c_str()); +} + +- (BOOL)isBookmark +{ + return m_info.IsBookmark(); +} + +- (BOOL)isApi +{ + return m_info.HasApiUrl(); +} + +- (BOOL)isBooking +{ + return m_info.IsHotel(); +} + +- (BOOL)isMyPosition +{ + return m_info.IsMyPosition(); +} + +- (BOOL)isHTMLDescription +{ + return strings::IsHTML(m_info.m_bookmarkDescription); +} + +#pragma mark - Coordinates + +- (m2::PointD const &)mercator +{ + return m_info.GetMercator(); +} + +- (ms::LatLon)latLon +{ + return m_info.GetLatLon(); +} + ++ (void)toggleCoordinateSystem +{ + NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; + [ud setBool:![ud boolForKey:kUserDefaultsLatLonAsDMSKey] forKey:kUserDefaultsLatLonAsDMSKey]; + [ud synchronize]; +} + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageEntity.h b/iphone/Maps/Classes/MWMPlacePageEntity.h index 9effcba53a..3f538c04b2 100644 --- a/iphone/Maps/Classes/MWMPlacePageEntity.h +++ b/iphone/Maps/Classes/MWMPlacePageEntity.h @@ -61,12 +61,13 @@ using MWMPlacePageCellTypeValueMap = map; - (BOOL)isBookmark; - (BOOL)isApi; - (BOOL)isBooking; -- (ms::LatLon)latlon; +- (ms::LatLon)latLon; - (m2::PointD const &)mercator; - (NSString *)apiURL; -- (NSURL *)bookingUrl; -- (NSURL *)bookingDescriptionUrl; +- (NSURL *)bookingURL; +- (NSURL *)bookingDescriptionURL; - (NSString * )hotelId; +- (NSString *)phoneNumber; - (string)titleForNewBookmark; - (instancetype)initWithInfo:(place_page::Info const &)info; diff --git a/iphone/Maps/Classes/MWMPlacePageEntity.mm b/iphone/Maps/Classes/MWMPlacePageEntity.mm index 47f95083f9..fc31f06d48 100644 --- a/iphone/Maps/Classes/MWMPlacePageEntity.mm +++ b/iphone/Maps/Classes/MWMPlacePageEntity.mm @@ -14,7 +14,7 @@ using feature::Metadata; -extern NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; +static NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; namespace { @@ -162,9 +162,9 @@ void initFieldsMap() - (void)configureBookmark { auto const bac = m_info.GetBookmarkAndCategory(); - BookmarkCategory * cat = GetFramework().GetBmCategory(bac.first); + BookmarkCategory * cat = GetFramework().GetBmCategory(bac.m_categoryIndex); BookmarkData const & data = - static_cast(cat->GetUserMark(bac.second))->GetData(); + static_cast(cat->GetUserMark(bac.m_bookmarkIndex))->GetData(); self.bookmarkTitle = @(data.GetName().c_str()); self.bookmarkCategory = @(m_info.GetBookmarkCategoryName().c_str()); @@ -214,11 +214,11 @@ void initFieldsMap() return haveField ? @(it->second.c_str()) : nil; } -- (NSURL *)bookingUrl +- (NSURL *)bookingURL { return [self sponsoredUrl:NO]; } -- (NSURL *)bookingDescriptionUrl { return [self sponsoredUrl:YES]; } +- (NSURL *)bookingDescriptionURL { return [self sponsoredUrl:YES]; } - (NSURL *)sponsoredUrl:(BOOL)isDescription { auto const & url = @@ -238,7 +238,12 @@ void initFieldsMap() return self.isBooking ? @(m_info.GetMetadata().Get(Metadata::FMD_SPONSORED_ID).c_str()) : nil; } -- (ms::LatLon)latlon { return m_info.GetLatLon(); } +- (NSString *)phoneNumber +{ + return [self getCellValue:MWMPlacePageCellTypePhoneNumber]; +} + +- (ms::LatLon)latLon { return m_info.GetLatLon(); } - (m2::PointD const &)mercator { return m_info.GetMercator(); } - (NSString *)apiURL { return @(m_info.GetApiUrl().c_str()); } - (string)titleForNewBookmark { return m_info.FormatNewBookmarkName(); } @@ -246,7 +251,7 @@ void initFieldsMap() { BOOL const useDMSFormat = [[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsLatLonAsDMSKey]; - ms::LatLon const latlon = self.latlon; + ms::LatLon const latlon = self.latLon; return @((useDMSFormat ? measurement_utils::FormatLatLon(latlon.lat, latlon.lon) : measurement_utils::FormatLatLonAsDMS(latlon.lat, latlon.lon, 2)) .c_str()); @@ -295,14 +300,14 @@ void initFieldsMap() - (void)synchronize { Framework & f = GetFramework(); - BookmarkCategory * category = f.GetBmCategory(self.bac.first); + BookmarkCategory * category = f.GetBmCategory(self.bac.m_categoryIndex); if (!category) return; { BookmarkCategory::Guard guard(*category); Bookmark * bookmark = - static_cast(guard.m_controller.GetUserMarkForEdit(self.bac.second)); + static_cast(guard.m_controller.GetUserMarkForEdit(self.bac.m_bookmarkIndex)); if (!bookmark) return; diff --git a/iphone/Maps/Classes/MWMPlacePageInfoCell.h b/iphone/Maps/Classes/MWMPlacePageInfoCell.h index 65c97eeaf0..daae2878a7 100644 --- a/iphone/Maps/Classes/MWMPlacePageInfoCell.h +++ b/iphone/Maps/Classes/MWMPlacePageInfoCell.h @@ -1,9 +1,13 @@ #import "MWMPlacePageEntity.h" #import "MWMTableViewCell.h" +#import "MWMPlacePageData.h" + @interface MWMPlacePageInfoCell : MWMTableViewCell -- (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info; +- (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info NS_DEPRECATED_IOS(7_0, 8_0); + +- (void)configWithRow:(place_page::MetainfoRows)row data:(MWMPlacePageData *)data NS_AVAILABLE_IOS(8_0); @property(weak, nonatomic, readonly) IBOutlet UIImageView * icon; @property(weak, nonatomic, readonly) IBOutlet id textContainer; diff --git a/iphone/Maps/Classes/MWMPlacePageInfoCell.mm b/iphone/Maps/Classes/MWMPlacePageInfoCell.mm index 1edbd5aa79..7c394bed07 100644 --- a/iphone/Maps/Classes/MWMPlacePageInfoCell.mm +++ b/iphone/Maps/Classes/MWMPlacePageInfoCell.mm @@ -19,7 +19,9 @@ @property(weak, nonatomic) IBOutlet UIButton * upperButton; @property(weak, nonatomic) IBOutlet UIImageView * toggleImage; -@property(nonatomic) MWMPlacePageCellType type; +@property(nonatomic) MWMPlacePageCellType type NS_DEPRECATED_IOS(7_0, 8_0); +@property(nonatomic) place_page::MetainfoRows rowType NS_AVAILABLE_IOS(8_0); +@property(weak, nonatomic) MWMPlacePageData * data NS_AVAILABLE_IOS(8_0); @end @@ -37,8 +39,55 @@ } } +- (void)configWithRow:(place_page::MetainfoRows)row data:(MWMPlacePageData *)data; +{ + self.rowType = row; + self.data = data; + NSString * name; + switch (row) + { + case place_page::MetainfoRows::Address: + self.toggleImage.hidden = YES; + name = @"address"; + break; + case place_page::MetainfoRows::Phone: + self.toggleImage.hidden = YES; + name = @"phone_number"; + break; + case place_page::MetainfoRows::Website: + self.toggleImage.hidden = YES; + name = @"website"; + break; + case place_page::MetainfoRows::Email: + self.toggleImage.hidden = YES; + name = @"email"; + break; + case place_page::MetainfoRows::Cuisine: + self.toggleImage.hidden = YES; + name = @"cuisine"; + break; + case place_page::MetainfoRows::Operator: + self.toggleImage.hidden = YES; + name = @"operator"; + break; + case place_page::MetainfoRows::Internet: + self.toggleImage.hidden = YES; + name = @"wifi"; + break; + case place_page::MetainfoRows::Coordinate: + self.toggleImage.hidden = NO; + name = @"coordinate"; + break; + case place_page::MetainfoRows::OpeningHours: + NSAssert(false, @"Incorrect cell type!"); + break; + } + [self configWithIconName:name data:[self.data stringForRow:row]]; +} + - (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info; { + self.type = type; NSString * typeName; switch (type) { @@ -70,14 +119,18 @@ default: NSAssert(false, @"Incorrect type!"); break; } + [self configWithIconName:typeName data:info]; +} + +- (void)configWithIconName:(NSString *)name data:(NSString *)data +{ UIImage * image = - [UIImage imageNamed:[NSString stringWithFormat:@"%@%@", @"ic_placepage_", typeName]]; - self.type = type; + [UIImage imageNamed:[NSString stringWithFormat:@"%@%@", @"ic_placepage_", name]]; self.icon.image = image; self.icon.mwm_coloring = [self.textContainer isKindOfClass:[UITextView class]] ? MWMImageColoringBlue : MWMImageColoringBlack; - [self changeText:info]; + [self changeText:data]; UILongPressGestureRecognizer * longTap = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longTap:)]; longTap.minimumPressDuration = 0.3; @@ -123,25 +176,55 @@ - (IBAction)cellTap { - switch (self.type) + if (isIOS7) { - case MWMPlacePageCellTypeURL: - case MWMPlacePageCellTypeWebsite: + switch (self.type) + { + case MWMPlacePageCellTypeURL: + case MWMPlacePageCellTypeWebsite: + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatOpenSite)]; + break; + case MWMPlacePageCellTypeEmail: + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatSendEmail)]; + break; + case MWMPlacePageCellTypePhoneNumber: + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)]; + break; + case MWMPlacePageCellTypeCoordinate: + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatToggleCoordinates)]; + [self.currentEntity toggleCoordinateSystem]; + [self changeText:[self.currentEntity getCellValue:MWMPlacePageCellTypeCoordinate]]; + break; + default: break; + } + return; + } + + switch (self.rowType) + { + case place_page::MetainfoRows::Phone: + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)]; + break; + case place_page::MetainfoRows::Website: [Statistics logEvent:kStatEventName(kStatPlacePage, kStatOpenSite)]; break; - case MWMPlacePageCellTypeEmail: + case place_page::MetainfoRows::Email: [Statistics logEvent:kStatEventName(kStatPlacePage, kStatSendEmail)]; break; - case MWMPlacePageCellTypePhoneNumber: - [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)]; - break; - case MWMPlacePageCellTypeCoordinate: + case place_page::MetainfoRows::Coordinate: [Statistics logEvent:kStatEventName(kStatPlacePage, kStatToggleCoordinates)]; - [self.currentEntity toggleCoordinateSystem]; - [self changeText:[self.currentEntity getCellValue:MWMPlacePageCellTypeCoordinate]]; + [MWMPlacePageData toggleCoordinateSystem]; + [self changeText:[self.data stringForRow:self.rowType]]; + break; + + case place_page::MetainfoRows::Cuisine: + case place_page::MetainfoRows::Operator: + case place_page::MetainfoRows::OpeningHours: + case place_page::MetainfoRows::Address: + case place_page::MetainfoRows::Internet: break; - default: break; } + } - (void)longTap:(UILongPressGestureRecognizer *)sender diff --git a/iphone/Maps/Classes/MWMPlacePageLayout.h b/iphone/Maps/Classes/MWMPlacePageLayout.h new file mode 100644 index 0000000000..e0e8d41aaa --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageLayout.h @@ -0,0 +1,42 @@ +#include "storage/storage_defines.hpp" + +@protocol MWMPlacePageLayoutDelegate + +- (void)onTopBoundChanged:(CGFloat)bound; +- (void)shouldDestroyLayout; + +@end + +@protocol MWMPlacePageLayoutDataSource + +- (NSString *)distanceToObject; +- (void)downloadSelectedArea; + +@end + +@class MWMPlacePageData, MWMPPView; +@protocol MWMPlacePageButtonsProtocol, MWMActionBarProtocol; + +/// Helps with place page view layout and representation +@interface MWMPlacePageLayout : NSObject + +- (instancetype)initWithOwnerView:(UIView *)view + delegate:(id)delegate + dataSource:(id)dataSource; + +- (void)layoutWithSize:(CGSize const &)size; +- (void)showWithData:(MWMPlacePageData *)data; +- (void)close; + +- (void)mwm_refreshUI; + +- (UIView *)shareAnchor; + +- (void)reloadBookmarkSection:(BOOL)isBookmark; + +- (void)rotateDirectionArrowToAngle:(CGFloat)angle; +- (void)setDistanceToObject:(NSString *)distance; + +- (void)processDownloaderEventWithStatus:(storage::NodeStatus)status progress:(CGFloat)progress; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageLayout.mm b/iphone/Maps/Classes/MWMPlacePageLayout.mm new file mode 100644 index 0000000000..b8e4a55a3c --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageLayout.mm @@ -0,0 +1,530 @@ +#import "MWMCircularProgress.h" +#import "MWMPlacePageActionBar.h" +#import "MWMPlacePageCellUpdateProtocol.h" +#import "MWMPlacePageData.h" +#import "MWMPlacePageLayout.h" +#import "MWMPPView.h" + +#import "MWMBookmarkCell.h" +#import "MWMPlacePageBookmarkCell.h" +#import "MWMPlacePageButtonCell.h" +#import "MWMPlacePageInfoCell.h" +#import "MWMOpeningHoursCell.h" +#import "MWMPlacePagePreviewCell.h" +#import "UIColor+MapsMeColor.h" + +#include "storage/storage.hpp" + +#include "std/array.hpp" + +namespace +{ +enum class ScrollDirection { Up, Down }; + +enum class State { Bottom, Top }; + +CGFloat const kMinOffset = 64; +CGFloat const kOpenPlacePageStopValue = 0.7; + +array kPreviewCells = {{@"MWMPlacePagePreviewCell"}}; + +array kBookmarkCells = {{@"MWMBookmarkCell"}}; + +array kMetaInfoCells = {{@"MWMOpeningHoursCell", @"PlacePageLinkCell", @"PlacePageInfoCell", @"PlacePageLinkCell", @"PlacePageLinkCell", @"PlacePageInfoCell", @"PlacePageInfoCell", @"PlacePageInfoCell", @"PlacePageInfoCell"}}; + +array kButtonsCells = {{@"MWMPlacePageButtonCell"}}; + +NSTimeInterval const kAnimationDuration = 0.15; + +void animate(TMWMVoidBlock animate, TMWMVoidBlock completion = nil) +{ + [UIView animateWithDuration:kAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ + animate(); + } completion:^(BOOL finished) { + if (completion) + completion(); + }]; +} + +} // namespace + +@interface MWMPlacePageLayout () + +@property(weak, nonatomic) MWMPlacePageData * data; + +@property(weak, nonatomic) UIView * ownerView; +@property(weak, nonatomic) id delegate; +@property(weak, nonatomic) id dataSource; + +@property(nonatomic) MWMPPScrollView * scrollView; +@property(nonatomic) IBOutlet MWMPPView * placePageView; + +@property(nonatomic) ScrollDirection direction; +@property(nonatomic) State state; + +@property(nonatomic) CGFloat portraitOpenContentOffset; +@property(nonatomic) CGFloat landscapeOpenContentOffset; +@property(nonatomic) CGFloat lastContentOffset; +@property(nonatomic) CGFloat expandedContentOffset; + +@property(nonatomic) MWMPlacePagePreviewCell * ppPreviewCell; +@property(nonatomic) MWMBookmarkCell * bookmarkCell; + +@property(nonatomic) MWMPlacePageActionBar * actionBar; + +@property(nonatomic) BOOL isPlacePageButtonsEnabled; +@property(nonatomic) BOOL isDownloaderViewShown; + +@end + +@implementation MWMPlacePageLayout + +- (instancetype)initWithOwnerView:(UIView *)view + delegate:(id)delegate + dataSource:(id)dataSource +{ + self = [super init]; + if (self) + { + _ownerView = view; + _delegate = delegate; + _dataSource = dataSource; + + [[NSBundle mainBundle] loadNibNamed:[MWMPPView className] owner:self options:nil]; + auto view = self.ownerView; + auto const & size = view.size; + _placePageView.frame = {{0, size.height}, size}; + _placePageView.delegate = self; + _scrollView = [[MWMPPScrollView alloc] initWithFrame:view.frame inactiveView:_placePageView]; + _portraitOpenContentOffset = MAX(size.width, size.height) * kOpenPlacePageStopValue; + _landscapeOpenContentOffset = MIN(size.width, size.height) * kOpenPlacePageStopValue; + [_ownerView addSubview:_scrollView]; + [_scrollView addSubview:_placePageView]; + + auto tv = _placePageView.tableView; + [tv registerNib:[UINib nibWithNibName:kPreviewCells[0] bundle:nil] forCellReuseIdentifier:kPreviewCells[0]]; + [tv registerNib:[UINib nibWithNibName:kButtonsCells[0] bundle:nil] forCellReuseIdentifier:kButtonsCells[0]]; + [tv registerNib:[UINib nibWithNibName:kBookmarkCells[0] bundle:nil] forCellReuseIdentifier:kBookmarkCells[0]]; + } + return self; +} + +- (void)registerCells +{ + auto tv = self.placePageView.tableView; + // Register all meta info cells. + for (auto const i : self.data.metainfoRows) + { + auto name = kMetaInfoCells[static_cast(i)]; + [tv registerNib:[UINib nibWithNibName:name bundle:nil] forCellReuseIdentifier:name]; + } +} + +- (void)layoutWithSize:(CGSize const &)size +{ + self.scrollView.frame = {{}, size}; + self.placePageView.origin = {0., size.height}; + self.actionBar.frame = {{0., size.height - 48}, {size.width, 48}}; + [self.delegate onTopBoundChanged:self.scrollView.contentOffset.y]; +} + +- (UIView *)shareAnchor +{ + return self.actionBar.shareAnchor; +} + +- (void)showWithData:(MWMPlacePageData *)data +{ + self.isPlacePageButtonsEnabled = YES; + self.data = nil; + self.ppPreviewCell = nil; + self.bookmarkCell = nil; + + self.scrollView.delegate = self; + self.state = State::Bottom; + + [self collapse]; + + self.data = data; + [self.actionBar configureWithData:static_cast>(self.data)]; + [self registerCells]; + [self.placePageView.tableView reloadData]; +} + +- (void)rotateDirectionArrowToAngle:(CGFloat)angle +{ + [self.ppPreviewCell rotateDirectionArrowToAngle:angle]; +} + +- (void)setDistanceToObject:(NSString *)distance +{ + [self.ppPreviewCell setDistanceToObject:distance]; +} + +- (MWMPlacePageActionBar *)actionBar +{ + if (!_actionBar) + { + _actionBar = [MWMPlacePageActionBar actionBarWithDelegate:self.delegate]; + UIView * superview = self.ownerView; + _actionBar.origin = {0., superview.height}; + [superview addSubview:_actionBar]; + } + return _actionBar; +} + +- (void)close +{ + animate(^ { + self.actionBar.origin = {0., self.ownerView.height}; + [self.scrollView setContentOffset:{} animated:YES]; + }, ^{ + [self.actionBar removeFromSuperview]; + self.actionBar = nil; + [self.delegate shouldDestroyLayout]; + }); +} + +- (void)mwm_refreshUI +{ + [self.placePageView mwm_refreshUI]; + [self.actionBar mwm_refreshUI]; +} + +- (void)reloadBookmarkSection:(BOOL)isBookmark +{ + auto tv = self.placePageView.tableView; + NSIndexSet * set = [NSIndexSet indexSetWithIndex:static_cast(place_page::Sections::Bookmark)]; + + if (isBookmark) + { + if (self.bookmarkCell) + [tv reloadSections:set withRowAnimation:UITableViewRowAnimationAutomatic]; + else + [tv insertSections:set withRowAnimation:UITableViewRowAnimationAutomatic]; + } + else + { + [tv deleteSections:set withRowAnimation:UITableViewRowAnimationAutomatic]; + self.bookmarkCell = nil; + } +} + +- (void)collapse +{ + self.actionBar.hidden = YES; + + [self.placePageView hideTableView:YES]; + self.scrollView.scrollEnabled = NO; + + animate(^{ + self.actionBar.origin = {0., self.ownerView.height}; + [self.scrollView setContentOffset:{0., kMinOffset} animated:YES]; + }); +} + +- (void)expand +{ + self.actionBar.hidden = NO; + self.scrollView.scrollEnabled = YES; + + animate(^{ + [self.placePageView hideTableView:NO]; + self.actionBar.minY = self.actionBar.superview.height - self.actionBar.height; + self.expandedContentOffset = self.ppPreviewCell.height + self.placePageView.top.height + self.actionBar.height; + auto const targetOffset = self.state == State::Bottom ? self.expandedContentOffset : self.topContentOffset; + [self.scrollView setContentOffset:{0, targetOffset} animated:YES]; + }); +} + +- (BOOL)isPortrait +{ + auto const & s = self.ownerView.size; + return s.height > s.width; +} + +- (CGFloat)topContentOffset +{ + auto const target = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset; + if (target > self.placePageView.height) + return self.placePageView.height; + + return target; +} + +#pragma mark - Downloader event + +- (void)processDownloaderEventWithStatus:(storage::NodeStatus)status progress:(CGFloat)progress +{ + using namespace storage; + + switch(status) + { + case NodeStatus::Undefined: + { + self.isPlacePageButtonsEnabled = YES; + auto const & sections = self.data.sections; + auto const it = find(sections.begin(), sections.end(), place_page::Sections::Buttons); + if (it != sections.end()) + { + [self.placePageView.tableView reloadSections:[NSIndexSet indexSetWithIndex:distance(sections.begin(), it)] + withRowAnimation:UITableViewRowAnimationAutomatic]; + } + + if (self.ppPreviewCell) + [self.ppPreviewCell setDownloaderViewHidden:YES animated:NO]; + else + self.isDownloaderViewShown = NO; + + break; + } + + case NodeStatus::Downloading: + self.ppPreviewCell.mapDownloadProgress.progress = progress; + break; + + case NodeStatus::InQueue: + self.ppPreviewCell.mapDownloadProgress.state = MWMCircularProgressStateSpinner; + break; + + case NodeStatus::Error: + self.ppPreviewCell.mapDownloadProgress.state = MWMCircularProgressStateFailed; + break; + + case NodeStatus::Partly: + break; + + case NodeStatus::OnDiskOutOfDate: + case NodeStatus::OnDisk: + { + self.isPlacePageButtonsEnabled = YES; + auto const & sections = self.data.sections; + auto const it = find(sections.begin(), sections.end(), place_page::Sections::Buttons); + if (it != sections.end()) + { + [self.placePageView.tableView reloadSections:[NSIndexSet indexSetWithIndex:distance(sections.begin(), it)] + withRowAnimation:UITableViewRowAnimationAutomatic]; + } + [self.ppPreviewCell setDownloaderViewHidden:YES animated:NO]; + break; + } + + case NodeStatus::NotDownloaded: + self.isPlacePageButtonsEnabled = NO; + if (self.ppPreviewCell) + [self.ppPreviewCell setDownloaderViewHidden:NO animated:NO]; + else + self.isDownloaderViewShown = YES; + + break; + } +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewDidScroll:(MWMPPScrollView *)scrollView +{ + if ([scrollView isEqual:self.placePageView.tableView]) + return; + + auto const offset = scrollView.contentOffset; + if (offset.y <= 0) + { + [self.scrollView removeFromSuperview]; + [self.actionBar removeFromSuperview]; + [self.delegate shouldDestroyLayout]; + return; + } + + if (offset.y > self.placePageView.height + 30) + { + auto const bounded = self.placePageView.height + 30; + [scrollView setContentOffset:{0, bounded}]; + [self.delegate onTopBoundChanged:bounded]; + } + else + { + [self.delegate onTopBoundChanged:offset.y]; + } + + self.direction = self.lastContentOffset < offset.y ? ScrollDirection::Up : ScrollDirection::Down; + self.lastContentOffset = offset.y; +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView + withVelocity:(CGPoint)velocity + targetContentOffset:(inout CGPoint *)targetContentOffset +{ + auto const actualOffset = scrollView.contentOffset.y; + auto const openOffset = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset; + auto const targetOffset = (*targetContentOffset).y; + + if (actualOffset > self.expandedContentOffset && actualOffset < openOffset) + { + self.state = self.direction == ScrollDirection::Up ? State::Top : State::Bottom; + (*targetContentOffset).y = self.direction == ScrollDirection::Up ? openOffset : self.expandedContentOffset; + } + else if (actualOffset > openOffset && targetOffset < openOffset) + { + self.state = State::Top; + (*targetContentOffset).y = openOffset; + } + else if (actualOffset < self.expandedContentOffset) + { + (*targetContentOffset).y = 0; + animate(^{ + self.actionBar.origin = {0., self.ownerView.height}; + }); + } + else + { + self.state = State::Top; + } +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if (decelerate) + return; + + auto const actualOffset = scrollView.contentOffset.y; + auto const openOffset = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset; + if (actualOffset < self.expandedContentOffset + 30) + { + self.state = State::Bottom; + animate(^{ + [scrollView setContentOffset:{0, self.expandedContentOffset} animated:YES]; + }); + } + else if (actualOffset < openOffset) + { + self.state = self.direction == ScrollDirection::Up ? State::Top : State::Bottom; + animate(^{ + [scrollView setContentOffset:{0, self.direction == ScrollDirection::Up ? openOffset : self.expandedContentOffset} + animated:YES]; + }); + } + else + { + self.state = State::Top; + } +} + +#pragma mark - UITableViewDelegate & UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + if (!self.data) + return 0; + return self.data.sections.size(); +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + using namespace place_page; + + switch (self.data.sections[section]) + { + case Sections::Preview: + case Sections::Bookmark: return 1; + case Sections::Metainfo: return self.data.metainfoRows.size(); + case Sections::Buttons: return self.data.buttonsRows.size(); + } +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + using namespace place_page; + + switch (self.data.sections[indexPath.section]) + { + case Sections::Preview: + { + if (!self.ppPreviewCell) + self.ppPreviewCell = [tableView dequeueReusableCellWithIdentifier:[MWMPlacePagePreviewCell className]]; + + [self.ppPreviewCell configure:self.data updateLayoutDelegate:self dataSource:self.dataSource]; + [self.ppPreviewCell setDownloaderViewHidden:!self.isDownloaderViewShown animated:NO]; + + return self.ppPreviewCell; + } + case Sections::Bookmark: + { + MWMBookmarkCell * c = [tableView dequeueReusableCellWithIdentifier:kBookmarkCells[0]]; + [c configureWithText:self.data.bookmarkDescription + updateCellDelegate:self + editBookmarkDelegate:self.delegate + isHTML:self.data.isHTMLDescription]; + return c; + } + case Sections::Metainfo: + { + auto const row = self.data.metainfoRows[indexPath.row]; + auto cellName = kMetaInfoCells[static_cast(row)]; + UITableViewCell * c = [tableView dequeueReusableCellWithIdentifier:cellName]; + + switch (row) + { + case MetainfoRows::OpeningHours: + [static_cast(c) configureWithOpeningHours:[self.data stringForRow:row] + updateLayoutDelegate:self + isClosedNow:self.data.schedule == OpeningHours::Closed]; + break; + case MetainfoRows::Phone: + case MetainfoRows::Address: + case MetainfoRows::Website: + case MetainfoRows::Email: + case MetainfoRows::Cuisine: + case MetainfoRows::Operator: + case MetainfoRows::Internet: + case MetainfoRows::Coordinate: + [static_cast(c) configWithRow:row data:self.data]; + break; + } + return c; + } + case Sections::Buttons: + { + MWMPlacePageButtonCell * c = [tableView dequeueReusableCellWithIdentifier:kButtonsCells[0]]; + auto const row = self.data.buttonsRows[indexPath.row]; + [c configForRow:row withDelegate:self.delegate]; + if (row != ButtonsRows::HotelDescription) + [c setEnabled:self.isPlacePageButtonsEnabled]; + + return c; + } + } +} + +#pragma mark - MWMPlacePageCellUpdateProtocol + +- (void)updateCellWithForceReposition:(BOOL)isForceReposition +{ + auto const update = isForceReposition ? @selector(updateWithExpand) : @selector(update); + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:update object:nil]; + [self performSelector:update withObject:nil afterDelay:0.5]; +} + +- (void)update +{ + auto tableView = self.placePageView.tableView; + [tableView beginUpdates]; + [tableView endUpdates]; +} + +- (void)updateWithExpand +{ + [self update]; + [self expand]; +} + +#pragma mark - MWMPlacePageViewUpdateProtocol + +- (void)updateWithHeight:(CGFloat)height +{ + auto const & size = self.ownerView.size; + self.scrollView.contentSize = {size.width, size.height + height}; +} + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageManager.h b/iphone/Maps/Classes/MWMPlacePageManager.h new file mode 100644 index 0000000000..65609662f7 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageManager.h @@ -0,0 +1,5 @@ +#import "MWMPlacePageProtocol.h" + +@interface MWMPlacePageManager : NSObject + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageManager.mm b/iphone/Maps/Classes/MWMPlacePageManager.mm new file mode 100644 index 0000000000..c4f6d65022 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageManager.mm @@ -0,0 +1,335 @@ +#import "MapViewController.h" +#import "MWMActivityViewController.h" +#import "MWMAPIBar.h" +#import "MWMCircularProgress.h" +#import "MWMPlacePageManager.h" +#import "MWMViewController.h" +#import "MWMPlacePageLayout.h" +#import "MWMPlacePageData.h" +#import "MWMFrameworkListener.h" +#import "MWMFrameworkObservers.h" +#import "MWMLocationManager.h" +#import "MWMRouter.h" +#import "MWMStorage.h" +#import "MWMEditBookmarkController.h" +#import "Statistics.h" +#import + +#include "geometry/distance_on_sphere.hpp" + +#include "platform/measurement_utils.hpp" + +@interface MWMPlacePageManager() + +@property(weak, nonatomic) MWMViewController * ownerViewController; +@property(nonatomic) MWMPlacePageEntity * entity; + +@property(nonatomic) MWMPlacePageLayout * layout; +@property(nonatomic) MWMPlacePageData * data; + +@property(nonatomic) storage::NodeStatus currentDownloaderStatus; + +@end + +@implementation MWMPlacePageManager + +- (instancetype)initWithViewController:(MWMViewController *)viewController +{ + self = [super init]; + if (self) + _ownerViewController = viewController; + return self; +} + +- (void)showPlacePage:(place_page::Info const &)info +{ + self.currentDownloaderStatus = storage::NodeStatus::Undefined; + [MWMFrameworkListener addObserver:self]; + + self.data = [[MWMPlacePageData alloc] initWithPlacePageInfo:info]; + [self.layout showWithData:self.data]; + + // Call for the first time to produce changes + [self processCountryEvent:self.data.countryId]; + + if (![MWMLocationManager lastHeading]) + return; + + [MWMLocationManager addObserver:self]; + [self.layout setDistanceToObject:self.distanceToObject]; +} + +- (void)closePlacePage +{ + [_layout close]; + [MWMLocationManager removeObserver:self]; + [MWMFrameworkListener removeObserver:self]; +} + +#pragma mark - MWMPlacePageLayoutDataSource + +- (void)downloadSelectedArea +{ + auto const & countryId = self.data.countryId; + NodeAttrs nodeAttrs; + GetFramework().GetStorage().GetNodeAttrs(countryId, nodeAttrs); + MWMAlertViewController * avc = [MapViewController controller].alertController; + switch (nodeAttrs.m_status) + { + case NodeStatus::NotDownloaded: + case NodeStatus::Partly: + [MWMStorage downloadNode:countryId alertController:avc onSuccess:nil]; + break; + case NodeStatus::Undefined: + case NodeStatus::Error: [MWMStorage retryDownloadNode:countryId]; break; + case NodeStatus::OnDiskOutOfDate: [MWMStorage updateNode:countryId alertController:avc]; break; + case NodeStatus::Downloading: + case NodeStatus::InQueue: [MWMStorage cancelDownloadNode:countryId]; break; + case NodeStatus::OnDisk: break; + } +} + +- (NSString *)distanceToObject +{ + CLLocation * lastLocation = [MWMLocationManager lastLocation]; + if (!lastLocation) + return @""; + string distance; + CLLocationCoordinate2D const coord = lastLocation.coordinate; + ms::LatLon const target = self.data.latLon; + measurement_utils::FormatDistance( + ms::DistanceOnEarth(coord.latitude, coord.longitude, target.lat, target.lon), distance); + return @(distance.c_str()); +} + +#pragma mark - MWMFrameworkStorageObserver + +- (void)processCountryEvent:(TCountryId const &)countryId +{ + if (countryId == kInvalidCountryId) + { + [_layout processDownloaderEventWithStatus:storage::NodeStatus::Undefined progress:0]; + return; + } + + if (self.data.countryId != countryId) + return; + + NodeStatuses statuses; + GetFramework().GetStorage().GetNodeStatuses(countryId, statuses); + + auto const status = statuses.m_status; + if (status == self.currentDownloaderStatus) + return; + + self.currentDownloaderStatus = status; + [_layout processDownloaderEventWithStatus:status progress:0]; +} + +- (void)processCountry:(TCountryId const &)countryId progress:(MapFilesDownloader::TProgress const &)progress +{ + if (countryId == kInvalidCountryId || self.data.countryId != countryId) + return; + + [_layout processDownloaderEventWithStatus:storage::NodeStatus::Downloading + progress:static_cast(progress.first) / progress.second]; +} + +#pragma mark - MWMPlacePageLayout + +- (MWMPlacePageLayout *)layout +{ + if (!_layout) + { + _layout = [[MWMPlacePageLayout alloc] initWithOwnerView:self.ownerViewController.view + delegate:self + dataSource:self]; + } + + return _layout; +} + +- (void)onTopBoundChanged:(CGFloat)bound +{ + [[MWMMapViewControlsManager manager] dragPlacePage:{{0, self.ownerViewController.view.height - bound}, {}}]; +} + +- (void)shouldDestroyLayout +{ + self.layout = nil; + GetFramework().DeactivateMapSelection(false); +} + +#pragma mark - MWMLocationObserver + +- (void)onHeadingUpdate:(location::CompassInfo const &)info +{ + CLLocation * lastLocation = [MWMLocationManager lastLocation]; + if (!lastLocation) + return; + + CGFloat const angle = ang::AngleTo(lastLocation.mercator, self.data.mercator) + info.m_bearing; + [_layout rotateDirectionArrowToAngle:angle]; +} + +- (void)onLocationUpdate:(location::GpsInfo const &)locationInfo +{ + [_layout setDistanceToObject:self.distanceToObject]; +} + +- (void)mwm_refreshUI +{ + [_layout mwm_refreshUI]; +} + +- (void)dismissPlacePage +{ + [self closePlacePage]; +} + +- (void)hidePlacePage +{ + [self closePlacePage]; +} + +- (void)routeFrom +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBuildRoute) + withParameters:@{kStatValue : kStatSource}]; + [[MWMRouter router] buildFromPoint:self.target bestRouter:YES]; + [self closePlacePage]; +} + +- (void)routeTo +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBuildRoute) + withParameters:@{kStatValue : kStatDestination}]; + [[MWMRouter router] buildToPoint:self.target bestRouter:YES]; + [self closePlacePage]; +} + +- (MWMRoutePoint)target +{ + NSString * name = nil; + if (self.data.title.length > 0) + name = self.data.title; + else if (self.data.address.length > 0) + name = self.data.address; + else if (self.data.subtitle.length > 0) + name = self.data.subtitle; + else if (self.data.isBookmark) + name = self.data.externalTitle; + else + name = L(@"placepage_unknown_place"); + + m2::PointD const & org = self.data.mercator; + return self.data.isMyPosition ? MWMRoutePoint(org) : MWMRoutePoint(org, name); +} + +- (void)share +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatShare)]; + MWMActivityViewController * shareVC = + [MWMActivityViewController shareControllerForPlacePageObject:static_cast>(self.data)]; + [shareVC presentInParentViewController:self.ownerViewController + anchorView:self.layout.shareAnchor]; +} + +- (void)editPlace +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatEdit)]; + [[PushNotificationManager pushManager] setTags:@{ @"editor_edit_discovered" : @YES }]; + [(MapViewController *)self.ownerViewController openEditor]; +} + +- (void)addBusiness +{ + [Statistics logEvent:kStatEditorAddClick withParameters:@{kStatValue : kStatPlacePage}]; + [[MWMMapViewControlsManager manager] addPlace:YES hasPoint:NO point:m2::PointD()]; +} + +- (void)addPlace +{ + [Statistics logEvent:kStatEditorAddClick + withParameters:@{kStatValue : kStatPlacePageNonBuilding}]; + [[MWMMapViewControlsManager manager] addPlace:NO hasPoint:YES point:self.data.mercator]; +} + + +- (void)addBookmark +{ + [self.data updateBookmarkStatus:YES]; + [self.layout reloadBookmarkSection:YES]; +} + +- (void)removeBookmark +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBookmarks) + withParameters:@{kStatValue : kStatRemove}]; + [self.data updateBookmarkStatus:NO]; + [self.layout reloadBookmarkSection:NO]; +} + +- (void)editBookmark +{ + [[MapViewController controller] openBookmarkEditorWithData:self.data]; +} + +- (void)book:(BOOL)isDescription +{ + NSMutableDictionary * stat = [@{ kStatProvider : kStatBooking } mutableCopy]; + MWMPlacePageData * data = self.data; + auto const latLon = data.latLon; + stat[kStatHotel] = data.hotelId; + stat[kStatHotelLat] = @(latLon.lat); + stat[kStatHotelLon] = @(latLon.lon); + [Statistics logEvent:isDescription ? kPlacePageHotelDetails : kPlacePageHotelBook + withParameters:stat + atLocation:[MWMLocationManager lastLocation]]; + + UIViewController * vc = static_cast([MapViewController controller]); + NSURL * url = isDescription ? self.data.bookingDescriptionURL : self.data.bookingURL; + NSAssert(url, @"Booking url can't be nil!"); + [vc openUrl:url]; +} + +- (void)call +{ + NSAssert(self.data.phoneNumber, @"Phone number can't be nil!"); + NSString * phoneNumber = [[@"telprompt:" stringByAppendingString:self.data.phoneNumber] + stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]]; +} + +- (void)apiBack +{ + [Statistics logEvent:kStatEventName(kStatPlacePage, kStatAPI)]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.data.apiURL]]; + [[MapViewController controller].apiBar back]; +} + +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator +{ + [coordinator animateAlongsideTransition:^(id context) { + [self->_layout layoutWithSize:size]; + } completion:^(id context) { }]; +} + +#pragma mark - MWMFeatureHolder + +- (FeatureID const &)featureId +{ + return self.data.featureId; +} + +#pragma mark - Deprecated + +@synthesize leftBound = _leftBound; +@synthesize topBound = _topBound; +- (void)setTopBound:(CGFloat)topBound {_topBound = 0;} +- (void)setLeftBound:(CGFloat)leftBound {_leftBound = 0;} +- (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller {} +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {} + +@end diff --git a/iphone/Maps/Classes/MWMPlacePagePreviewCell.h b/iphone/Maps/Classes/MWMPlacePagePreviewCell.h new file mode 100644 index 0000000000..dde52ca55b --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePagePreviewCell.h @@ -0,0 +1,24 @@ +#import "MWMTableViewCell.h" + +@class MWMPlacePageData; +@class MWMCircularProgress; + +@protocol MWMPlacePageCellUpdateProtocol; +@protocol MWMPlacePageLayoutDataSource; + +@interface MWMPlacePagePreviewCell : MWMTableViewCell + +- (void)setDistanceToObject:(NSString *)distance; +- (void)rotateDirectionArrowToAngle:(CGFloat)angle; + +- (void)setDownloaderViewHidden:(BOOL)isHidden animated:(BOOL)isAnimated; +- (void)setDownloadingProgress:(CGFloat)progress; + +- (void)configure:(MWMPlacePageData *)data + updateLayoutDelegate:(id)delegate + dataSource:(id)dataSource; + + +- (MWMCircularProgress *)mapDownloadProgress; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm b/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm new file mode 100644 index 0000000000..952140042d --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm @@ -0,0 +1,426 @@ +#import "Common.h" +#import "MWMDirectionView.h" +#import "MWMPlacePageCellUpdateProtocol.h" +#import "MWMPlacePageData.h" +#import "MWMPlacePageLayout.h" +#import "MWMPlacePagePreviewCell.h" +#import "MWMCircularProgress.h" +#import "UIColor+MapsmeColor.h" + +#include "std/array.hpp" +#include "std/vector.hpp" + +namespace +{ +array kPPPClasses = {{@"_MWMPPPTitle", + @"_MWMPPPExternalTitle", + @"_MWMPPPSubtitle", + @"_MWMPPPSchedule", + @"_MWMPPPBooking", + @"_MWMPPPAddress"}}; + +enum class Labels +{ + Title, + ExternalTitle, + Subtitle, + Schedule, + Booking, + Address +}; + +void * kContext = &kContext; +NSString * const kTableViewContentSizeKeyPath = @"contentSize"; +CGFloat const kDefaultTableViewLeading = 16; +CGFloat const kCompressedTableViewLeading = 56; + +} // namespace + +#pragma mark - Base + +// Base class for avoiding copy-paste in inheriting cells. +@interface _MWMPPPCellBase : MWMTableViewCell + +@property(weak, nonatomic) IBOutlet UILabel * distance; +@property(weak, nonatomic) IBOutlet UIImageView * compass; +@property(weak, nonatomic) IBOutlet UIView * distanceView; +@property(weak, nonatomic) IBOutlet NSLayoutConstraint * trailing; +@property(copy, nonatomic) TMWMVoidBlock tapOnDistance; + +@end + +@implementation _MWMPPPCellBase + +- (IBAction)tap +{ + if (self.tapOnDistance) + self.tapOnDistance(); +} + +@end + +#pragma mark - Title + +@interface _MWMPPPTitle : _MWMPPPCellBase + +@property(weak, nonatomic) IBOutlet UILabel * title; + +@end + +@implementation _MWMPPPTitle +@end + +#pragma mark - External Title + +@interface _MWMPPPExternalTitle : _MWMPPPCellBase + +@property(weak, nonatomic) IBOutlet UILabel * externalTitle; + +@end + +@implementation _MWMPPPExternalTitle +@end + +#pragma mark - Subtitle + +@interface _MWMPPPSubtitle : _MWMPPPCellBase + +@property(weak, nonatomic) IBOutlet UILabel * subtitle; + +@end + +@implementation _MWMPPPSubtitle +@end + +#pragma mark - Schedule + +@interface _MWMPPPSchedule : _MWMPPPCellBase + +@property(weak, nonatomic) IBOutlet UILabel * schedule; + +@end + +@implementation _MWMPPPSchedule +@end + +#pragma mark - Booking + +@interface _MWMPPPBooking : MWMTableViewCell + +@property(weak, nonatomic) IBOutlet UILabel * rating; +@property(weak, nonatomic) IBOutlet UILabel * pricing; + +@end + +@implementation _MWMPPPBooking +@end + +#pragma mark - Address + +@interface _MWMPPPAddress : _MWMPPPCellBase + +@property(weak, nonatomic) IBOutlet UILabel * address; + +@end + +@implementation _MWMPPPAddress +@end + +#pragma mark - Public + +@interface MWMPlacePagePreviewCell () +{ + vector m_cells; +} + +@property(weak, nonatomic) IBOutlet UITableView * tableView; +@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewHeight; +@property(weak, nonatomic) NSLayoutConstraint * trailing; +@property(weak, nonatomic) UIView * distanceView; + +@property(weak, nonatomic) IBOutlet UIView * downloaderParentView; +@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewLeading; + +@property(nonatomic) MWMCircularProgress * mapDownloadProgress; + +@property(nonatomic) BOOL isDirectionViewAvailable; + +@property(weak, nonatomic) MWMPlacePageData * data; +@property(weak, nonatomic) id delegate; +@property(weak, nonatomic) id dataSource; + +@property(copy, nonatomic) NSString * distance; +@property(weak, nonatomic) UIImageView * compass; +@property(nonatomic) CGFloat currentContentHeight; + +@property(nonatomic) MWMDirectionView * directionView; + +@end + +@implementation MWMPlacePagePreviewCell + +- (void)awakeFromNib +{ + [super awakeFromNib]; + for (auto const s : kPPPClasses) + [self.tableView registerNib:[UINib nibWithNibName:s bundle:nil] forCellReuseIdentifier:s]; + + self.tableView.estimatedRowHeight = 20; + self.tableView.rowHeight = UITableViewAutomaticDimension; + [self registerObserver]; +} + +- (void)dealloc +{ + [self unregisterObserver]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == kContext) + { + NSValue * s = change[@"new"]; + CGFloat const height = s.CGSizeValue.height; + if (abs(height - self.currentContentHeight) > 0.5) + { + self.currentContentHeight = height; + self.tableViewHeight.constant = height; + [self setNeedsLayout]; + [self.delegate updateCellWithForceReposition:YES]; + } + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (MWMDirectionView *)directionView +{ + if (!_directionView) + _directionView = [[MWMDirectionView alloc] initWithManager:nil]; + return _directionView; +} + +- (void)setIsDirectionViewAvailable:(BOOL)isDirectionViewAvailable +{ + if (_isDirectionViewAvailable == isDirectionViewAvailable) + return; + _isDirectionViewAvailable = isDirectionViewAvailable; + [self setNeedsLayout]; +} + +- (void)rotateDirectionArrowToAngle:(CGFloat)angle +{ + self.compass.layer.transform = CATransform3DMakeRotation(M_PI_2 - angle, 0, 0, 1); + self.directionView.directionArrow.layer.transform = CATransform3DMakeRotation(M_PI_2 - angle, 0, 0, 1); +} + +- (void)setDistanceToObject:(NSString *)distance +{ + if (!distance.length) + { + self.isDirectionViewAvailable = NO; + [self setNeedsLayout]; + return; + } + + if ([self.distance isEqualToString:distance]) + return; + + self.distance = distance; + self.directionView.distanceLabel.text = distance; + self.isDirectionViewAvailable = YES; +} + +- (void)unregisterObserver +{ + [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext]; +} + +- (void)registerObserver +{ + [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext]; +} + +- (void)setDownloadingProgress:(CGFloat)progress +{ + self.mapDownloadProgress.progress = progress; +} + +- (void)setDownloaderViewHidden:(BOOL)isHidden animated:(BOOL)isAnimated +{ + self.downloaderParentView.hidden = isHidden; + self.tableViewLeading.constant = isHidden ? kDefaultTableViewLeading : kCompressedTableViewLeading; + [self setNeedsLayout]; + + if (!isHidden) + self.mapDownloadProgress.state = MWMCircularProgressStateNormal; + + if (!isAnimated) + return; + + [UIView animateWithDuration:kDefaultAnimationDuration animations:^{ + [self layoutIfNeeded]; + }]; +} + +- (void)configure:(MWMPlacePageData *)data + updateLayoutDelegate:(id)delegate + dataSource:(id)dataSource +{ + self.data = data; + self.delegate = delegate; + self.dataSource = dataSource; + [self setDistanceToObject:dataSource.distanceToObject]; + + m_cells.clear(); + + if (data.title.length) + m_cells.push_back(Labels::Title); + + if (data.externalTitle.length) + m_cells.push_back(Labels::ExternalTitle); + + if (data.subtitle.length) + m_cells.push_back(Labels::Subtitle); + + if (data.schedule != place_page::OpeningHours::Unknown) + m_cells.push_back(Labels::Schedule); + + if (data.bookingRating.length) + m_cells.push_back(Labels::Booking); + + if (data.address.length) + m_cells.push_back(Labels::Address); + + [self.tableView reloadData]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return m_cells.size(); +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + auto data = self.data; + _MWMPPPCellBase * c = nil; + BOOL const isNeedToShowDistance = self.isDirectionViewAvailable && (indexPath.row == m_cells.size() - 1); + + switch (m_cells[indexPath.row]) + { + case Labels::Title: + { + c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPTitle className]]; + static_cast<_MWMPPPTitle *>(c).title.text = data.title; + break; + } + case Labels::ExternalTitle: + { + c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPExternalTitle className]]; + static_cast<_MWMPPPExternalTitle *>(c).externalTitle.text = data.externalTitle; + break; + } + case Labels::Subtitle: + { + c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPSubtitle className]]; + static_cast<_MWMPPPSubtitle *>(c).subtitle.text = data.subtitle; + break; + } + case Labels::Schedule: + { + c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPSchedule className]]; + auto castedCell = static_cast<_MWMPPPSchedule *>(c); + switch(data.schedule) + { + case place_page::OpeningHours::AllDay: + castedCell.schedule.text = L(@"twentyfour_seven"); + castedCell.schedule.textColor = [UIColor blackSecondaryText]; + break; + case place_page::OpeningHours::Open: + castedCell.schedule.text = L(@"editor_time_open"); + castedCell.schedule.textColor = [UIColor blackSecondaryText]; + break; + case place_page::OpeningHours::Closed: + castedCell.schedule.text = L(@"closed_now"); + castedCell.schedule.textColor = [UIColor red]; + break; + case place_page::OpeningHours::Unknown: + NSAssert(false, @"Incorrect schedule!"); + break; + } + break; + } + case Labels::Booking: + { + _MWMPPPBooking * c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPBooking className]]; + c.rating.text = data.bookingRating; + c.pricing.text = data.bookingApproximatePricing; + [data assignOnlinePriceToLabel:c.pricing]; + return c; + } + case Labels::Address: + { + c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPAddress className]]; + static_cast<_MWMPPPAddress *>(c).address.text = data.address; + break; + } + } + + if (isNeedToShowDistance) + [self showDistanceOnCell:c]; + else + [self hideDistanceOnCell:c]; + + return c; +} + +- (void)showDistanceOnCell:(_MWMPPPCellBase *)cell +{ + cell.trailing.priority = UILayoutPriorityDefaultLow; + cell.distance.text = self.distance; + cell.tapOnDistance = ^{ [self.directionView show]; }; + [cell.contentView setNeedsLayout]; + self.compass = cell.compass; + self.trailing = cell.trailing; + self.distanceView = cell.distanceView; + cell.distanceView.hidden = NO; +} + +- (void)hideDistanceOnCell:(_MWMPPPCellBase *)cell +{ + cell.trailing.priority = UILayoutPriorityDefaultHigh; + [cell.contentView setNeedsLayout]; + cell.distanceView.hidden = YES; +} + +#pragma mark - MWMCircularProgressProtocol + +- (void)progressButtonPressed:(MWMCircularProgress *)progress +{ + [self.dataSource downloadSelectedArea]; +} + +#pragma mark - Properties + +- (MWMCircularProgress *)mapDownloadProgress +{ + if (!_mapDownloadProgress) + { + _mapDownloadProgress = + [MWMCircularProgress downloaderProgressForParentView:self.downloaderParentView]; + _mapDownloadProgress.delegate = self; + + MWMCircularProgressStateVec const affectedStates = {MWMCircularProgressStateNormal, + MWMCircularProgressStateSelected}; + + [_mapDownloadProgress setImage:[UIImage imageNamed:@"ic_download"] forStates:affectedStates]; + [_mapDownloadProgress setColoring:MWMButtonColoringBlue forStates:affectedStates]; + } + return _mapDownloadProgress; +} + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageProtocol.h b/iphone/Maps/Classes/MWMPlacePageProtocol.h new file mode 100644 index 0000000000..af0e6a4c35 --- /dev/null +++ b/iphone/Maps/Classes/MWMPlacePageProtocol.h @@ -0,0 +1,58 @@ +#import "MWMMapViewControlsManager.h" + +#include "Framework.h" + +@class MWMPlacePageEntity, MWMViewController; + +@protocol MWMActionBarProtocol + +- (void)routeFrom; +- (void)routeTo; + +- (void)share; + +- (void)addBookmark; +- (void)removeBookmark; + +- (void)call; +- (void)book:(BOOL)isDecription; + +- (void)apiBack; + +@end + +@protocol MWMPlacePageButtonsProtocol + +- (void)editPlace; +- (void)addPlace; +- (void)addBusiness; +- (void)book:(BOOL)isDescription; +- (void)editBookmark; + +@end + +struct FeatureID; + +@protocol MWMFeatureHolder + +- (FeatureID const &)featureId; + +@end + +@protocol MWMPlacePageProtocol + +@property(weak, nonatomic, readonly) MWMViewController * ownerViewController; +@property(nonatomic) CGFloat topBound; +@property(nonatomic) CGFloat leftBound; + +- (instancetype)initWithViewController:(MWMViewController *)viewController; +- (void)showPlacePage:(place_page::Info const &)info; +- (void)mwm_refreshUI; +- (void)dismissPlacePage; +- (void)hidePlacePage; +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation; +- (void)viewWillTransitionToSize:(CGSize)size + withTransitionCoordinator:(id)coordinator; +- (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller; + +@end diff --git a/iphone/Maps/Classes/MWMPlacePageViewManager.h b/iphone/Maps/Classes/MWMPlacePageViewManager.h index 9f23e20ead..37e46f782a 100644 --- a/iphone/Maps/Classes/MWMPlacePageViewManager.h +++ b/iphone/Maps/Classes/MWMPlacePageViewManager.h @@ -2,16 +2,16 @@ #include "Framework.h" +#include "MWMPlacePageProtocol.h" + @class MWMPlacePageEntity, MWMPlacePageNavigationBar, MWMViewController; -@interface MWMPlacePageViewManager : NSObject +@interface MWMPlacePageViewManager : NSObject @property(weak, nonatomic, readonly) MWMViewController * ownerViewController; @property(nonatomic, readonly) MWMPlacePageEntity * entity; -@property(nonatomic) MWMPlacePageNavigationBar * iPhoneNavigationBar; @property(nonatomic) CGFloat topBound; @property(nonatomic) CGFloat leftBound; -@property(nonatomic, readonly) BOOL isDirectionViewShown; - (instancetype)initWithViewController:(MWMViewController *)viewController; - (void)showPlacePage:(place_page::Info const &)info; @@ -37,7 +37,6 @@ - (void)reloadBookmark; - (void)dragPlacePage:(CGRect)frame; - (void)showDirectionViewWithTitle:(NSString *)title type:(NSString *)type; -- (void)hideDirectionView; - (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller; - (void)changeHeight:(CGFloat)height; diff --git a/iphone/Maps/Classes/MWMPlacePageViewManager.mm b/iphone/Maps/Classes/MWMPlacePageViewManager.mm index d886d64d42..f5033f8128 100644 --- a/iphone/Maps/Classes/MWMPlacePageViewManager.mm +++ b/iphone/Maps/Classes/MWMPlacePageViewManager.mm @@ -70,6 +70,11 @@ extern NSString * const kBookmarksChangedNotification; [self configPlacePage]; } +- (FeatureID const &)featureId +{ + return self.entity.featureID; +} + #pragma mark - Layout - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation @@ -211,7 +216,7 @@ extern NSString * const kBookmarksChangedNotification; [Alohalytics logEvent:kAlohalyticsTapEventKey withValue:@"ppShare"]; MWMPlacePageEntity * entity = self.entity; MWMActivityViewController * shareVC = - [MWMActivityViewController shareControllerForPlacePageObject:entity]; + [MWMActivityViewController shareControllerForPlacePageObject:static_cast>(entity)]; [shareVC presentInParentViewController:self.ownerViewController anchorView:self.placePage.actionBar.shareAnchor]; } @@ -220,7 +225,7 @@ extern NSString * const kBookmarksChangedNotification; { NSMutableDictionary * stat = [@{ kStatProvider : kStatBooking } mutableCopy]; MWMPlacePageEntity * en = self.entity; - auto const latLon = en.latlon; + auto const latLon = en.latLon; stat[kStatHotel] = en.hotelId; stat[kStatHotelLat] = @(latLon.lat); stat[kStatHotelLon] = @(latLon.lon); @@ -229,7 +234,7 @@ extern NSString * const kBookmarksChangedNotification; atLocation:[MWMLocationManager lastLocation]]; UIViewController * vc = static_cast([MapViewController controller]); - NSURL * url = isDescription ? self.entity.bookingDescriptionUrl : self.entity.bookingUrl; + NSURL * url = isDescription ? self.entity.bookingDescriptionURL : self.entity.bookingURL; NSAssert(url, @"Booking url can't be nil!"); [vc openUrl:url]; } @@ -276,10 +281,10 @@ extern NSString * const kBookmarksChangedNotification; withParameters:@{kStatValue : kStatAdd}]; Framework & f = GetFramework(); BookmarkData bmData = {self.entity.titleForNewBookmark, f.LastEditedBMType()}; - size_t const categoryIndex = f.LastEditedBMCategory(); - size_t const bookmarkIndex = - f.GetBookmarkManager().AddBookmark(categoryIndex, self.entity.mercator, bmData); - self.entity.bac = {categoryIndex, bookmarkIndex}; + auto const categoryIndex = static_cast(f.LastEditedBMCategory()); + auto const bookmarkIndex = + static_cast(f.GetBookmarkManager().AddBookmark(categoryIndex, self.entity.mercator, bmData)); + self.entity.bac = {bookmarkIndex, categoryIndex}; self.entity.bookmarkTitle = @(bmData.GetName().c_str()); self.entity.bookmarkCategory = @(f.GetBmCategory(categoryIndex)->GetName().c_str()); [NSNotificationCenter.defaultCenter postNotificationName:kBookmarksChangedNotification @@ -294,16 +299,16 @@ extern NSString * const kBookmarksChangedNotification; [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBookmarks) withParameters:@{kStatValue : kStatRemove}]; Framework & f = GetFramework(); - BookmarkCategory * bookmarkCategory = f.GetBookmarkManager().GetBmCategory(self.entity.bac.first); + BookmarkCategory * bookmarkCategory = f.GetBookmarkManager().GetBmCategory(self.entity.bac.m_categoryIndex); if (bookmarkCategory) { { BookmarkCategory::Guard guard(*bookmarkCategory); - guard.m_controller.DeleteUserMark(self.entity.bac.second); + guard.m_controller.DeleteUserMark(self.entity.bac.m_bookmarkIndex); } bookmarkCategory->SaveToKMLFile(); } - self.entity.bac = MakeEmptyBookmarkAndCategory(); + self.entity.bac = {}; self.entity.bookmarkTitle = nil; self.entity.bookmarkCategory = nil; [NSNotificationCenter.defaultCenter postNotificationName:kBookmarksChangedNotification @@ -335,7 +340,7 @@ extern NSString * const kBookmarksChangedNotification; return @""; string distance; CLLocationCoordinate2D const coord = lastLocation.coordinate; - ms::LatLon const target = self.entity.latlon; + ms::LatLon const target = self.entity.latLon; measurement_utils::FormatDistance( ms::DistanceOnEarth(coord.latitude, coord.longitude, target.lat, target.lon), distance); return @(distance.c_str()); @@ -344,26 +349,12 @@ extern NSString * const kBookmarksChangedNotification; - (void)showDirectionViewWithTitle:(NSString *)title type:(NSString *)type { MWMDirectionView * directionView = self.directionView; - UIView * ownerView = self.ownerViewController.view; directionView.titleLabel.text = title; directionView.typeLabel.text = type; - [ownerView addSubview:directionView]; - [ownerView endEditing:YES]; - [directionView setNeedsLayout]; - MapsAppDelegate * app = [MapsAppDelegate theApp]; - [app.mapViewController updateStatusBarStyle]; - [app disableStandby]; + [directionView show]; [self updateDistance]; } -- (void)hideDirectionView -{ - [self.directionView removeFromSuperview]; - MapsAppDelegate * app = [MapsAppDelegate theApp]; - [app.mapViewController updateStatusBarStyle]; - [app enableStandby]; -} - - (void)changeHeight:(CGFloat)height { if (!IPAD) @@ -399,7 +390,7 @@ extern NSString * const kBookmarksChangedNotification; return _directionView; } -- (BOOL)isDirectionViewShown { return self.directionView.superview != nil; } - (void)setTopBound:(CGFloat)topBound { _topBound = self.placePage.topBound = topBound; } - (void)setLeftBound:(CGFloat)leftBound { _leftBound = self.placePage.leftBound = leftBound; } +- (void)editBookmark {} @end diff --git a/iphone/Maps/Classes/MapViewController.h b/iphone/Maps/Classes/MapViewController.h index 2bde266871..aaa01fb826 100644 --- a/iphone/Maps/Classes/MapViewController.h +++ b/iphone/Maps/Classes/MapViewController.h @@ -12,6 +12,7 @@ struct AddressInfo; @class MWMMapViewControlsManager; @class MWMAPIBar; +@class MWMPlacePageData; @interface MapViewController : MWMViewController @@ -31,6 +32,7 @@ struct AddressInfo; - (void)openBookmarks; - (void)openMapsDownloader:(mwm::DownloaderMode)mode; - (void)openEditor; +- (void)openBookmarkEditorWithData:(MWMPlacePageData *)data; - (void)initialize; diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index acb07db231..6448ee8cdf 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -19,7 +19,9 @@ #import "MWMMapDownloaderViewController.h" #import "MWMMapViewControlsManager.h" #import "MWMPageController.h" +#import "MWMPlacePageData.h" #import "MWMPlacePageEntity.h" +#import "MWMPlacePageProtocol.h" #import "MWMRouter.h" #import "MWMRouterSavedState.h" #import "MWMSettings.h" @@ -70,6 +72,8 @@ NSString * const kDownloaderSegue = @"Map2MapDownloaderSegue"; NSString * const kMigrationSegue = @"Map2MigrationSegue"; NSString * const kEditorSegue = @"Map2EditorSegue"; NSString * const kUDViralAlertWasShown = @"ViralAlertWasShown"; +NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; + // The first launch after process started. Used to skip "Not follow, no position" state and to run // locator. @@ -416,7 +420,8 @@ BOOL gIsFirstMyPositionMode = YES; - (void)openEditor { using namespace osm_auth_ios; - auto const & featureID = self.controlsManager.placePageEntity.info.GetID(); + + auto const & featureID = self.controlsManager.featureHolder.featureId; [Statistics logEvent:kStatEditorEditStart withParameters:@{ @@ -425,7 +430,12 @@ BOOL gIsFirstMyPositionMode = YES; kStatEditorMWMName : @(featureID.GetMwmName().c_str()), kStatEditorMWMVersion : @(featureID.GetMwmVersion()) }]; - [self performSegueWithIdentifier:kEditorSegue sender:self.controlsManager.placePageEntity]; + [self performSegueWithIdentifier:kEditorSegue sender:self.controlsManager.featureHolder]; +} + +- (void)openBookmarkEditorWithData:(MWMPlacePageData *)data +{ + [self performSegueWithIdentifier:kPP2BookmarkEditingSegue sender:data]; } - (void)processMyPositionStateModeEvent:(location::EMyPositionMode)mode @@ -552,7 +562,12 @@ BOOL gIsFirstMyPositionMode = YES; if ([segue.identifier isEqualToString:kEditorSegue]) { MWMEditorViewController * dvc = segue.destinationViewController; - [dvc setFeatureToEdit:static_cast(sender).featureID]; + [dvc setFeatureToEdit:static_cast>(sender).featureId]; + } + else if ([segue.identifier isEqualToString:kPP2BookmarkEditingSegue]) + { + MWMEditBookmarkController * dvc = segue.destinationViewController; + dvc.data = static_cast(sender); } else if ([segue.identifier isEqualToString:kDownloaderSegue]) { diff --git a/iphone/Maps/Classes/PlacePageActionBar.xib b/iphone/Maps/Classes/PlacePageActionBar.xib deleted file mode 100644 index 0d4a6fe67d..0000000000 --- a/iphone/Maps/Classes/PlacePageActionBar.xib +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iphone/Maps/Classes/Share/MWMActivityViewController.h b/iphone/Maps/Classes/Share/MWMActivityViewController.h index 14b2d9f266..46e6741fbe 100644 --- a/iphone/Maps/Classes/Share/MWMActivityViewController.h +++ b/iphone/Maps/Classes/Share/MWMActivityViewController.h @@ -1,4 +1,4 @@ -@class MWMPlacePageEntity; +@protocol MWMPlacePageObject; @interface MWMActivityViewController : UIActivityViewController @@ -6,7 +6,7 @@ + (instancetype)shareControllerForMyPosition:(CLLocationCoordinate2D const &)location; -+ (instancetype)shareControllerForPlacePageObject:(MWMPlacePageEntity *)entity; ++ (instancetype)shareControllerForPlacePageObject:(id)object; - (void)presentInParentViewController:(UIViewController *)parentVC anchorView:(UIView *)anchorView; diff --git a/iphone/Maps/Classes/Share/MWMActivityViewController.mm b/iphone/Maps/Classes/Share/MWMActivityViewController.mm index a9e9237019..40b689b646 100644 --- a/iphone/Maps/Classes/Share/MWMActivityViewController.mm +++ b/iphone/Maps/Classes/Share/MWMActivityViewController.mm @@ -32,10 +32,10 @@ return [[self alloc] initWithActivityItem:item]; } -+ (instancetype)shareControllerForPlacePageObject:(MWMPlacePageEntity *)entity ++ (instancetype)shareControllerForPlacePageObject:(id)object; { MWMShareActivityItem * item = - [[MWMShareActivityItem alloc] initForPlacePageObjectWithEntity:entity]; + [[MWMShareActivityItem alloc] initForPlacePageObject:object]; return [[self alloc] initWithActivityItem:item]; } diff --git a/iphone/Maps/Classes/Share/MWMShareActivityItem.h b/iphone/Maps/Classes/Share/MWMShareActivityItem.h index 22be5da8eb..8e7b0826b8 100644 --- a/iphone/Maps/Classes/Share/MWMShareActivityItem.h +++ b/iphone/Maps/Classes/Share/MWMShareActivityItem.h @@ -1,8 +1,25 @@ -@class MWMPlacePageEntity; +namespace ms +{ +class LatLon; + +} // namespace ms + +@protocol MWMPlacePageObject + +- (BOOL)isMyPosition; +- (BOOL)isBooking; +- (NSString *)title; +- (NSString *)subtitle; +- (NSString *)address; +- (NSURL *)bookingDescriptionURL; +- (NSString *)phoneNumber; +- (ms::LatLon)latLon; + +@end @interface MWMShareActivityItem : NSObject - (instancetype)initForMyPositionAtLocation:(CLLocationCoordinate2D const &)location; -- (instancetype)initForPlacePageObjectWithEntity:(MWMPlacePageEntity *)entity; +- (instancetype)initForPlacePageObject:(id)object; @end diff --git a/iphone/Maps/Classes/Share/MWMShareActivityItem.mm b/iphone/Maps/Classes/Share/MWMShareActivityItem.mm index b68bf35518..1693884555 100644 --- a/iphone/Maps/Classes/Share/MWMShareActivityItem.mm +++ b/iphone/Maps/Classes/Share/MWMShareActivityItem.mm @@ -15,7 +15,7 @@ NSString * httpGe0Url(NSString * shortUrl) @interface MWMShareActivityItem () -@property(nonatomic) MWMPlacePageEntity * entity; +@property(nonatomic) id object; @property(nonatomic) CLLocationCoordinate2D location; @property(nonatomic) BOOL isMyPosition; @@ -34,16 +34,16 @@ NSString * httpGe0Url(NSString * shortUrl) return self; } -- (instancetype)initForPlacePageObjectWithEntity:(MWMPlacePageEntity *)entity +- (instancetype)initForPlacePageObject:(id)object { self = [super init]; if (self) { - NSAssert(entity, @"Entity can't be nil!"); - BOOL const isMyPosition = entity.isMyPosition; + NSAssert(object, @"Entity can't be nil!"); + BOOL const isMyPosition = object.isMyPosition; _isMyPosition = isMyPosition; if (!isMyPosition) - _entity = entity; + _object = object; } return self; } @@ -52,22 +52,22 @@ NSString * httpGe0Url(NSString * shortUrl) { auto & f = GetFramework(); - auto const title = ^NSString *(MWMPlacePageEntity * entity) + auto const title = ^NSString *(id obj) { - if (!entity || entity.isMyPosition) + if (!obj || obj.isMyPosition) return L(@"my_position"); - else if (entity.title.length) - return entity.title; - else if (entity.subtitle.length) - return entity.subtitle; - else if (entity.address.length) - return entity.address; + else if (obj.title.length) + return obj.title; + else if (obj.subtitle.length) + return obj.subtitle; + else if (obj.address.length) + return obj.address; else return @""; }; - ms::LatLon const ll = self.entity ? self.entity.latlon : ms::LatLon(self.location.latitude, self.location.longitude); - string const s = f.CodeGe0url(ll.lat, ll.lon, f.GetDrawScale(), title(self.entity).UTF8String); + ms::LatLon const ll = self.object ? self.object.latLon : ms::LatLon(self.location.latitude, self.location.longitude); + string const s = f.CodeGe0url(ll.lat, ll.lon, f.GetDrawScale(), title(self.object).UTF8String); NSString * url = @(s.c_str()); if (!isShort) @@ -108,7 +108,7 @@ NSString * httpGe0Url(NSString * shortUrl) NSString * shortUrl = [self url:YES]; return [NSString stringWithFormat:@"%@\n%@", httpGe0Url(shortUrl), self.isMyPosition ? L(@"my_position_share_email_subject") - : self.entity.title]; + : self.object.title]; } - (NSString *)itemDefaultWithActivityType:(NSString *)activityType @@ -123,13 +123,13 @@ NSString * httpGe0Url(NSString * shortUrl) } NSMutableString * result = [L(@"sharing_call_action_look") mutableCopy]; - vector strings{self.entity.title, self.entity.subtitle, self.entity.address, - [self.entity getCellValue:MWMPlacePageCellTypePhoneNumber], url}; + vector strings{self.object.title, self.object.subtitle, self.object.address, + self.object.phoneNumber, url}; - if (self.entity.isBooking) + if (self.object.isBooking) { strings.push_back(L(@"sharing_booking")); - strings.push_back(self.entity.bookingDescriptionUrl.absoluteString); + strings.push_back(self.object.bookingDescriptionURL.absoluteString); } for (auto const str : strings) -- cgit v1.2.3