From 63176abb8ea534f0dc5c678d345c690afa7fba37 Mon Sep 17 00:00:00 2001 From: Daria Volvenkova Date: Wed, 7 Aug 2019 20:03:35 +0300 Subject: [PATCH] [bookmarks][iOS] Refactoring. --- iphone/Maps/Bookmarks/BookmarksSection.h | 53 +- iphone/Maps/Bookmarks/BookmarksSection.mm | 145 +- iphone/Maps/Bookmarks/BookmarksVC.mm | 1188 +++++++++-------- iphone/Maps/Bookmarks/InfoSection.h | 14 + iphone/Maps/Bookmarks/InfoSection.mm | 51 + iphone/Maps/Bookmarks/MWMCategoryInfoCell.h | 2 +- .../Maps/Bookmarks/TableSectionDataSource.h | 21 + iphone/Maps/Bookmarks/TracksSection.h | 26 + iphone/Maps/Bookmarks/TracksSection.mm | 90 ++ iphone/Maps/Maps.xcodeproj/project.pbxproj | 14 + 10 files changed, 833 insertions(+), 771 deletions(-) create mode 100644 iphone/Maps/Bookmarks/InfoSection.h create mode 100644 iphone/Maps/Bookmarks/InfoSection.mm create mode 100644 iphone/Maps/Bookmarks/TableSectionDataSource.h create mode 100644 iphone/Maps/Bookmarks/TracksSection.h create mode 100644 iphone/Maps/Bookmarks/TracksSection.mm diff --git a/iphone/Maps/Bookmarks/BookmarksSection.h b/iphone/Maps/Bookmarks/BookmarksSection.h index bed87c2011..fdfa08c679 100644 --- a/iphone/Maps/Bookmarks/BookmarksSection.h +++ b/iphone/Maps/Bookmarks/BookmarksSection.h @@ -1,67 +1,24 @@ -#import "MWMTypes.h" - -#include "platform/location.hpp" +#import "TableSectionDataSource.h" #include "kml/type_utils.hpp" -@protocol TableSectionDelegate -- (NSInteger)numberOfRows; -- (NSString *)title; -- (BOOL)canEdit; -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row; -- (BOOL)didSelectRow:(NSInteger)row; -- (void)deleteRow:(NSInteger)row; - -@optional --(void)updateCell:(UITableViewCell *)cell forRow:(NSInteger)row withNewLocation:(location::GpsInfo const &)gpsInfo; -@end - @class BookmarksSection; @protocol BookmarksSectionDelegate + - (NSInteger)numberOfBookmarksInSection:(BookmarksSection *)bookmarkSection; - (NSString *)titleOfBookmarksSection:(BookmarksSection *)bookmarkSection; - (BOOL)canEditBookmarksSection:(BookmarksSection *)bookmarkSection; - (kml::MarkId)bookmarkSection:(BookmarksSection *)bookmarkSection getBookmarkIdByRow:(NSInteger)row; -- (void)bookmarkSection:(BookmarksSection *)bookmarkSection onDeleteBookmarkInRow:(NSInteger)row; +- (BOOL)bookmarkSection:(BookmarksSection *)bookmarkSection onDeleteBookmarkInRow:(NSInteger)row; + @end -@class TracksSection; - -@protocol TracksSectionDelegate -- (NSInteger)numberOfTracksInSection:(TracksSection *)tracksSection; -- (NSString *)titleOfTracksSection:(TracksSection *)tracksSection; -- (BOOL)canEditTracksSection:(TracksSection *)tracksSection; -- (kml::MarkId)tracksSection:(TracksSection *)tracksSection getTrackIdByRow:(NSInteger)row; -- (void)tracksSection:(TracksSection *)tracksSection onDeleteTrackInRow:(NSInteger)row; -@end - -@protocol InfoSectionDelegate -- (UITableViewCell *)infoCellForTableView:(UITableView *)tableView; -@end - -@interface BookmarksSection : NSObject +@interface BookmarksSection : NSObject @property (nullable, nonatomic) NSNumber * blockIndex; - (instancetype)initWithDelegate:(id)delegate; - - (instancetype)initWithBlockIndex:(NSNumber *)blockIndex delegate:(id)delegate; @end - -@interface TracksSection : NSObject - -@property (nullable, nonatomic) NSNumber * blockIndex; - -- (instancetype)initWithDelegate:(id)delegate; - -- (instancetype)initWithBlockIndex:(NSNumber *)blockIndex delegate: (id)delegate; - -@end - -@interface InfoSection : NSObject - -- (instancetype)initWithDelegate:(id)delegate; - -@end diff --git a/iphone/Maps/Bookmarks/BookmarksSection.mm b/iphone/Maps/Bookmarks/BookmarksSection.mm index c559f016c3..7f1a37f9f2 100644 --- a/iphone/Maps/Bookmarks/BookmarksSection.mm +++ b/iphone/Maps/Bookmarks/BookmarksSection.mm @@ -2,7 +2,6 @@ #import "CircleView.h" #import "ColorPickerView.h" #import "MWMBookmarksManager.h" -#import "MWMCategoryInfoCell.h" #import "MWMLocationHelpers.h" #import "MWMSearchManager.h" #include "Framework.h" @@ -80,8 +79,10 @@ CGFloat const kPinDiameter = 22.0f; { UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"BookmarksVCBookmarkItemCell"]; if (!cell) + { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"BookmarksVCBookmarkItemCell"]; + } CHECK(cell, ("Invalid bookmark cell.")); kml::MarkId const bmId = [self.delegate bookmarkSection:self getBookmarkIdByRow:row]; @@ -119,149 +120,11 @@ CGFloat const kPinDiameter = 22.0f; return YES; } -- (void)deleteRow: (NSInteger)row +- (BOOL)deleteRow: (NSInteger)row { kml::MarkId const bmId = [self.delegate bookmarkSection:self getBookmarkIdByRow:row]; [[MWMBookmarksManager sharedManager] deleteBookmark:bmId]; - [self.delegate bookmarkSection:self onDeleteBookmarkInRow:row]; -} - -@end - -//////////////////////////////////////////////////////// - -@interface TracksSection() - -@property (weak, nonatomic) id delegate; - -@end - -@implementation TracksSection - -- (instancetype)initWithDelegate:(id)delegate -{ - return [self initWithBlockIndex:nil delegate:delegate]; -} - -- (instancetype)initWithBlockIndex:(NSNumber *)blockIndex delegate: (id)delegate -{ - self = [super init]; - if (self) - { - _blockIndex = blockIndex; - _delegate = delegate; - } - return self; -} - -- (NSInteger)numberOfRows -{ - return [self.delegate numberOfTracksInSection:self]; -} - -- (NSString *)title -{ - return [self.delegate titleOfTracksSection:self]; -} - -- (BOOL)canEdit -{ - return [self.delegate canEditTracksSection:self]; -} - -- (UITableViewCell *)tableView: (UITableView *)tableView cellForRow: (NSInteger)row -{ - UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"TrackCell"]; - if (!cell) - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"TrackCell"]; - CHECK(cell, ("Invalid track cell.")); - - auto const & bm = GetFramework().GetBookmarkManager(); - - kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; - Track const * track = bm.GetTrack(trackId); - cell.textLabel.text = @(track->GetName().c_str()); - string dist; - if (measurement_utils::FormatDistance(track->GetLengthMeters(), dist)) - //Change Length before release!!! - cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", L(@"length"), @(dist.c_str())]; - else - cell.detailTextLabel.text = nil; - dp::Color const c = track->GetColor(0); - cell.imageView.image = [CircleView createCircleImageWith:kPinDiameter - andColor:[UIColor colorWithRed:c.GetRed()/255.f - green:c.GetGreen()/255.f - blue:c.GetBlue()/255.f - alpha:1.f]]; - return cell; -} - -- (BOOL)didSelectRow: (NSInteger)row -{ - kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; - GetFramework().ShowTrack(trackId); - return YES; -} - -- (void)deleteRow: (NSInteger)row -{ - // TODO(@darina): [[MWMBookmarksManager sharedManager] deleteTrack:bmId];? - kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; - auto & bm = GetFramework().GetBookmarkManager(); - bm.GetEditSession().DeleteTrack(trackId); - [self.delegate tracksSection:self onDeleteTrackInRow:row]; -} - -@end - -//////////////////////////////////////////////////////// - -@interface InfoSection() - -@property (weak, nonatomic) id delegate; - -@end - -@implementation InfoSection - -- (instancetype)initWithDelegate: (id)delegate -{ - self = [super init]; - if (self) - { - _delegate = delegate; - } - return self; -} - -- (NSInteger)numberOfRows -{ - return 1; -} - -- (NSString *)title -{ - return L(@"placepage_place_description"); -} - -- (BOOL)canEdit -{ - return NO; -} - -- (UITableViewCell *)tableView: (UITableView *)tableView cellForRow: (NSInteger)row -{ - return [self.delegate infoCellForTableView:tableView]; -} - -- (BOOL)didSelectRow: (NSInteger)row -{ - return NO; -} - -- (void)deleteRow: (NSInteger)row -{ - + return [self.delegate bookmarkSection:self onDeleteBookmarkInRow:row]; } @end diff --git a/iphone/Maps/Bookmarks/BookmarksVC.mm b/iphone/Maps/Bookmarks/BookmarksVC.mm index 76f4a02a00..b9e8e7f86c 100644 --- a/iphone/Maps/Bookmarks/BookmarksVC.mm +++ b/iphone/Maps/Bookmarks/BookmarksVC.mm @@ -1,16 +1,13 @@ #import "BookmarksVC.h" #import "BookmarksSection.h" -#import "CircleView.h" -#import "ColorPickerView.h" #import "MWMBookmarksManager.h" #import "MWMCommon.h" #import "MWMKeyboard.h" -#import "MWMLocationHelpers.h" #import "MWMLocationObserver.h" -#import "MWMSearchManager.h" #import "MWMSearchNoResults.h" #import "MWMCategoryInfoCell.h" #import "SwiftBridge.h" +#import "TracksSection.h" #include "Framework.h" @@ -39,9 +36,8 @@ using namespace std; BookmarksSharingViewControllerDelegate, CategorySettingsViewControllerDelegate> { - NSMutableArray> * m_sectionsCollection; + NSMutableArray> * m_sectionsCollection; BookmarkManager::SortedBlocksCollection m_sortedBlocks; - search::BookmarksSearchParams::Results m_searchResults; } @@ -49,6 +45,7 @@ using namespace std; @property(nonatomic) NSUInteger lastSortId; @property(nonatomic) BOOL infoExpanded; + @property(weak, nonatomic) IBOutlet UIView * statusBarBackground; @property(weak, nonatomic) IBOutlet UISearchBar * searchBar; @property(weak, nonatomic) IBOutlet UIView * noResultsContainer; @@ -61,6 +58,7 @@ using namespace std; @property(weak, nonatomic) IBOutlet NSLayoutConstraint * showSearchBar; @property(weak, nonatomic) IBOutlet UITableView * tableView; + @property(weak, nonatomic) IBOutlet UIToolbar * myCategoryToolbar; @property(weak, nonatomic) IBOutlet UIToolbar * downloadedCategoryToolbar; @property(weak, nonatomic) IBOutlet UIBarButtonItem * viewOnMapItem; @@ -81,7 +79,7 @@ using namespace std; if (self) { m_categoryId = index; - m_sectionsCollection = @[].mutableCopy; + m_sectionsCollection = [NSMutableArray array]; [self calculateSections]; } return self; @@ -97,391 +95,40 @@ using namespace std; return ![self isSearchMode] && !m_sortedBlocks.empty(); } -- (MWMSearchNoResults *)noResultsView +- (void)calculateSections { - if (!_noResultsView) - { - _noResultsView = [MWMSearchNoResults viewWithImage:[UIImage imageNamed:@"img_search_not_found"] - title:L(@"search_not_found") - text:L(@"search_not_found_query")]; - } - return _noResultsView; -} - -- (void)showNoResultsView:(BOOL)show -{ - if (!show) - { - self.tableView.hidden = NO; - self.noResultsContainer.hidden = YES; - [self.noResultsView removeFromSuperview]; - } - else - { - self.tableView.hidden = YES; - self.noResultsContainer.hidden = NO; - [self.noResultsContainer addSubview:self.noResultsView]; - self.noResultsView.translatesAutoresizingMaskIntoConstraints = NO; - [NSLayoutConstraint activateConstraints:@[ - [self.noResultsView.topAnchor constraintEqualToAnchor:self.noResultsContainer.topAnchor], - [self.noResultsView.leftAnchor constraintEqualToAnchor:self.noResultsContainer.leftAnchor], - [self.noResultsView.bottomAnchor constraintEqualToAnchor:self.noResultsContainer.bottomAnchor], - [self.noResultsView.rightAnchor constraintEqualToAnchor:self.noResultsContainer.rightAnchor], - ]]; - [self onKeyboardAnimation]; - } -} - -- (UIActivityIndicatorView *)spinner -{ - if (!_spinner) - { - _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - _spinner.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; - _spinner.hidesWhenStopped = YES; - } - return _spinner; -} - -- (UIActivityIndicatorView *)sortSpinner -{ - if (!_sortSpinner) - { - _sortSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - _sortSpinner.hidesWhenStopped = YES; - } - return _sortSpinner; -} - -- (UIImageView *)searchIcon -{ - if (!_searchIcon) - { - _searchIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_search"]]; - _searchIcon.mwm_coloring = MWMImageColoringBlack; - } - return _searchIcon; -} - -- (void)showSpinner:(BOOL)show -{ - UITextField * textField = [self.searchBar valueForKey:@"searchField"]; - if (!show) - { - textField.leftView = self.searchIcon; - [self.spinner stopAnimating]; - } - else - { - self.spinner.bounds = textField.leftView.bounds; - textField.leftView = self.spinner; - [self.spinner startAnimating]; - } -} - -- (void)showSortSpinner:(BOOL)show -{ - if (show) - [self.sortSpinner startAnimating]; - else - [self.sortSpinner stopAnimating]; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return [m_sectionsCollection count]; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return [m_sectionsCollection[section] numberOfRows]; -} - -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - return [m_sectionsCollection[section] title]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell * cell = [m_sectionsCollection[indexPath.section] tableView:tableView cellForRow:indexPath.row]; + [m_sectionsCollection removeAllObjects]; - cell.backgroundColor = [UIColor white]; - cell.textLabel.textColor = [UIColor blackPrimaryText]; - cell.detailTextLabel.textColor = [UIColor blackSecondaryText]; - return cell; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - // Remove cell selection - [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; - - auto const close = [m_sectionsCollection[indexPath.section] didSelectRow:indexPath.row]; - - [self.searchBar resignFirstResponder]; - - if (close) - [self.navigationController popToRootViewControllerAnimated:NO]; -} - -- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath -{ - return [m_sectionsCollection[indexPath.section] canEdit]; -} - -- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath -{ - self.editing = YES; -} - -- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath -{ - self.editing = NO; -} - -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (![m_sectionsCollection[indexPath.section] canEdit]) - return; - - if (editingStyle == UITableViewCellEditingStyleDelete) - [m_sectionsCollection[indexPath.section] deleteRow:indexPath.row]; - - auto const previousNumberOfSections = [m_sectionsCollection count]; - [self calculateSections]; - - //We can delete the row with animation, if number of sections stay the same. - if (previousNumberOfSections == [m_sectionsCollection count]) - [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; - else - [self.tableView reloadData]; - - // TODO(@darina): check - auto const & bm = GetFramework().GetBookmarkManager(); - if (bm.GetUserMarkIds(m_categoryId).size() + bm.GetTrackIds(m_categoryId).size() == 0) - { - self.navigationItem.rightBarButtonItem = nil; - [self setEditing:NO animated:YES]; - } -} - -- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section -{ - auto header = (UITableViewHeaderFooterView *)view; - header.textLabel.textColor = [UIColor blackSecondaryText]; - header.textLabel.font = [UIFont medium14]; -} - -#pragma mark - BookmarksSectionDelegate - -- (NSInteger)numberOfBookmarksInSection:(BookmarksSection *)bookmarkSection -{ if ([self isSearchMode]) { - return m_searchResults.size(); - } - - if ([self isSortMode]) - { - CHECK(bookmarkSection.blockIndex != nil, ()); - NSInteger index = bookmarkSection.blockIndex.integerValue; - return m_sortedBlocks[index].m_markIds.size(); - } - auto const & bm = GetFramework().GetBookmarkManager(); - return bm.GetUserMarkIds(m_categoryId).size(); -} - -- (NSString *)titleOfBookmarksSection:(BookmarksSection *)bookmarkSection -{ - if ([self isSearchMode]) - return nil; - - if ([self isSortMode]) - { - CHECK(bookmarkSection.blockIndex != nil, ()); - NSInteger index = bookmarkSection.blockIndex.integerValue; - return @(m_sortedBlocks[index].m_blockName.c_str()); - } - - return L(@"bookmarks"); -} - -- (BOOL)canEditBookmarksSection:(BookmarksSection *)bookmarkSection -{ - return [[MWMBookmarksManager sharedManager] isCategoryEditable:m_categoryId]; -} - -- (kml::MarkId)bookmarkSection:(BookmarksSection *)bookmarkSection getBookmarkIdByRow:(NSInteger)row -{ - if ([self isSearchMode]) - { - CHECK_LESS(row, m_searchResults.size(), ()); - return m_searchResults[row]; - } - - if ([self isSortMode]) - { - CHECK(bookmarkSection.blockIndex != nil, ()); - NSInteger index = bookmarkSection.blockIndex.integerValue; - return m_sortedBlocks[index].m_markIds[row]; - } - - auto const & bm = GetFramework().GetBookmarkManager(); - auto const & bookmarkIds = bm.GetUserMarkIds(m_categoryId); - ASSERT_LESS(row, bookmarkIds.size(), ()); - auto it = bookmarkIds.begin(); - advance(it, row); - return *it; -} - -- (void)bookmarkSection:(BookmarksSection *)bookmarkSection onDeleteBookmarkInRow:(NSInteger)row -{ - if ([self isSearchMode]) - { - CHECK_LESS(row, m_searchResults.size(), ()); - m_searchResults.erase(m_searchResults.begin() + row); - m_sortedBlocks.clear(); + [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithDelegate:self]]; return; } if ([self isSortMode]) { - CHECK(bookmarkSection.blockIndex != nil, ()); - NSInteger index = bookmarkSection.blockIndex.integerValue; - auto & marks = m_sortedBlocks[index].m_markIds; - marks.erase(marks.begin() + row); - if (marks.empty()) - m_sortedBlocks.erase(m_sortedBlocks.begin() + index); - } -} - -#pragma mark - TracksSectionDelegate - -- (NSInteger)numberOfTracksInSection:(TracksSection *)tracksSection -{ - CHECK(![self isSearchMode], ()); - - if ([self isSortMode]) - { - CHECK(tracksSection.blockIndex != nil, ()); - NSInteger index = tracksSection.blockIndex.integerValue; - return m_sortedBlocks[index].m_trackIds.size(); + NSInteger blockIndex = 0; + for (auto const & block : m_sortedBlocks) + { + if (!block.m_markIds.empty()) + [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithBlockIndex:@(blockIndex++) delegate:self]]; + } + return; } auto const & bm = GetFramework().GetBookmarkManager(); - return bm.GetTrackIds(m_categoryId).size(); -} - -- (NSString *)titleOfTracksSection:(TracksSection *)tracksSection -{ - CHECK(![self isSearchMode], ()); - - if ([self isSortMode]) + if (bm.IsCategoryFromCatalog(m_categoryId)) { - CHECK(tracksSection.blockIndex != nil, ()); - NSInteger index = tracksSection.blockIndex.integerValue; - return @(m_sortedBlocks[index].m_blockName.c_str()); + [m_sectionsCollection addObject:[[InfoSection alloc] initWithDelegate:self]]; } - return L(@"tracks_title"); -} - -- (BOOL)canEditTracksSection:(TracksSection *)tracksSection -{ - CHECK(![self isSearchMode], ()); - - if ([self isSortMode]) - return false; - - return [[MWMBookmarksManager sharedManager] isCategoryEditable:m_categoryId]; -} - -- (kml::TrackId)tracksSection:(TracksSection *)tracksSection getTrackIdByRow:(NSInteger)row -{ - CHECK(![self isSearchMode], ()); - - if ([self isSortMode]) + if (bm.GetTrackIds(m_categoryId).size() > 0) { - CHECK(tracksSection.blockIndex != nil, ()); - NSInteger index = tracksSection.blockIndex.integerValue; - return m_sortedBlocks[index].m_trackIds[row]; + [m_sectionsCollection addObject:[[TracksSection alloc] initWithDelegate:self]]; } - auto const & bm = GetFramework().GetBookmarkManager(); - auto const & trackIds = bm.GetTrackIds(m_categoryId); - ASSERT_LESS(row, trackIds.size(), ()); - auto it = trackIds.begin(); - advance(it, row); - return *it; -} - -- (void)tracksSection:(TracksSection *)tracksSection onDeleteTrackInRow:(NSInteger)row -{ - CHECK(![self isSearchMode], ()); - - if ([self isSortMode]) - { - CHECK(tracksSection.blockIndex != nil, ()); - NSInteger index = tracksSection.blockIndex.integerValue; - auto & tracks = m_sortedBlocks[index].m_trackIds; - tracks.erase(tracks.begin() + row); - if (tracks.empty()) - m_sortedBlocks.erase(m_sortedBlocks.begin() + index); - } -} - -#pragma mark - InfoSectionDelegate - -- (UITableViewCell *)infoCellForTableView: (UITableView *)tableView -{ - UITableViewCell * cell = [tableView dequeueReusableCellWithCellClass:MWMCategoryInfoCell.class]; - CHECK(cell, ("Invalid category info cell.")); - - auto & f = GetFramework(); - auto & bm = f.GetBookmarkManager(); - bool const categoryExists = bm.HasBmCategory(m_categoryId); - CHECK(categoryExists, ("Nonexistent category")); - - auto infoCell = (MWMCategoryInfoCell *)cell; - auto const & categoryData = bm.GetCategoryData(m_categoryId); - [infoCell updateWithCategoryData:categoryData delegate:self]; - infoCell.expanded = self.infoExpanded; - - return cell; -} - -#pragma mark - MWMCategoryInfoCellDelegate - -- (void)categoryInfoCellDidPressMore:(MWMCategoryInfoCell *)cell -{ - [self.tableView beginUpdates]; - cell.expanded = YES; - [self.tableView endUpdates]; - self.infoExpanded = YES; -} - -#pragma mark - MWMLocationObserver - -- (void)onLocationUpdate:(location::GpsInfo const &)info -{ - [self.tableView.visibleCells enumerateObjectsUsingBlock:^(UITableViewCell * cell, NSUInteger idx, BOOL * stop) - { - auto indexPath = [self.tableView indexPathForCell:cell]; - - // TODO(@darina): Is it fast? - if ([self->m_sectionsCollection[indexPath.section] respondsToSelector:@selector(updateCell:forRow:withNewLocation:)]) - [self->m_sectionsCollection[indexPath.section] updateCell:cell forRow:indexPath.row withNewLocation:info]; - }]; -} - -//*********** End of Location manager callbacks ******************** -//****************************************************************** - -- (UIStatusBarStyle)preferredStatusBarStyle -{ - setStatusBarBackgroundColor(UIColor.clearColor); - return UIStatusBarStyleLightContent; + if (bm.GetUserMarkIds(m_categoryId).size() > 0) + [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithDelegate:self]]; } - (void)viewDidLoad @@ -499,10 +146,6 @@ using namespace std; self.showSearchBar.priority = searchAllowed ? UILayoutPriorityRequired : UILayoutPriorityDefaultLow; self.hideSearchBar.priority = searchAllowed ? UILayoutPriorityDefaultLow : UILayoutPriorityRequired; - - //UITextField * textFiled = [self.searchBar valueForKey:@"searchField"]; - //UILabel * placeholder = [textFiled valueForKey:@"_placeholderLabel"]; - //placeholder.textColor = [UIColor blackHintText]; [self.noResultsView setTranslatesAutoresizingMaskIntoConstraints:NO]; @@ -602,53 +245,6 @@ using namespace std; [super viewDidDisappear:animated]; } -- (void)setEditing:(BOOL)editing animated:(BOOL)animated -{ - [super setEditing:editing animated:animated]; - [self.tableView setEditing:editing animated:animated]; -} - -- (NSString *)categoryFileName -{ - return @(GetFramework().GetBookmarkManager().GetCategoryFileName(m_categoryId).c_str()); -} - -- (void)calculateSections -{ - [m_sectionsCollection removeAllObjects]; - - if ([self isSearchMode]) - { - [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithDelegate:self]]; - return; - } - - if ([self isSortMode]) - { - NSInteger blockIndex = 0; - for (auto const & block : m_sortedBlocks) - { - if (!block.m_markIds.empty()) - [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithBlockIndex:@(blockIndex++) delegate:self]]; - } - return; - } - - auto const & bm = GetFramework().GetBookmarkManager(); - if (bm.IsCategoryFromCatalog(m_categoryId)) - { - [m_sectionsCollection addObject:[[InfoSection alloc] initWithDelegate:self]]; - } - - if (bm.GetTrackIds(m_categoryId).size() > 0) - { - [m_sectionsCollection addObject:[[TracksSection alloc] initWithDelegate:self]]; - } - - if (bm.GetUserMarkIds(m_categoryId).size() > 0) - [m_sectionsCollection addObject:[[BookmarksSection alloc] initWithDelegate:self]]; -} - - (IBAction)onMore:(UIBarButtonItem *)sender { auto actionSheet = [UIAlertController alertControllerWithTitle:nil @@ -709,6 +305,107 @@ using namespace std; [Statistics logEvent:kStatBookmarksListItemSettings withParameters:@{kStatOption : kStatMore}]; } +- (IBAction)onViewOnMap:(UIBarButtonItem *)sender +{ + [self viewOnMap]; +} + +- (void)openCategorySettings +{ + auto storyboard = [UIStoryboard instance:MWMStoryboardCategorySettings]; + auto settingsController = (CategorySettingsViewController *)[storyboard instantiateInitialViewController]; + settingsController.delegate = self; + settingsController.category = [[MWMBookmarksManager sharedManager] categoryWithId:m_categoryId]; + [self.navigationController pushViewController:settingsController animated:YES]; +} + +- (void)exportFile +{ + [[MWMBookmarksManager sharedManager] addObserver:self]; + [[MWMBookmarksManager sharedManager] shareCategory:m_categoryId]; +} + +- (void)shareCategory +{ + auto storyboard = [UIStoryboard instance:MWMStoryboardSharing]; + auto shareController = (BookmarksSharingViewController *)[storyboard instantiateInitialViewController]; + shareController.delegate = self; + shareController.category = [[MWMBookmarksManager sharedManager] categoryWithId:m_categoryId]; + [self.navigationController pushViewController:shareController animated:YES]; +} + +- (void)viewOnMap +{ + [self.navigationController popToRootViewControllerAnimated:YES]; + GetFramework().ShowBookmarkCategory(m_categoryId); +} + +- (IBAction)onSort:(UIBarButtonItem *)sender +{ + auto actionSheet = [UIAlertController alertControllerWithTitle:nil + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + + auto const sortingTypes = [self getAvailableSortingTypes]; + + for (auto type : sortingTypes) + { + [actionSheet addAction:[UIAlertAction actionWithTitle:[BookmarksVC getLocalizedSortingType:type] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + auto & bm = GetFramework().GetBookmarkManager(); + bm.SetLastSortingType(self->m_categoryId, type); + [self sort:type]; + }]]; + } + + [actionSheet addAction:[UIAlertAction actionWithTitle:L(@"sort_default") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + [self sortDefault]; + }]]; + + [actionSheet addAction:[UIAlertAction actionWithTitle:L(@"cancel") + style:UIAlertActionStyleCancel + handler:nil]]; + + actionSheet.popoverPresentationController.barButtonItem = self.sortItem; + [self presentViewController:actionSheet animated:YES completion:^{ + actionSheet.popoverPresentationController.passthroughViews = nil; + }]; +} + ++ (NSString *)getLocalizedSortingType:(BookmarkManager::SortingType)type +{ + switch (type) + { + case BookmarkManager::SortingType::ByTime: return L(@"sort_date"); + case BookmarkManager::SortingType::ByDistance: return L(@"sort_distance"); + case BookmarkManager::SortingType::ByType: return L(@"sort_type"); + } + UNREACHABLE(); +} + +- (std::vector)getAvailableSortingTypes +{ + CLLocation * lastLocation = [MWMLocationManager lastLocation]; + bool const hasMyPosition = lastLocation != nil; + auto const & bm = GetFramework().GetBookmarkManager(); + auto const sortingTypes = bm.GetAvailableSortingTypes(m_categoryId, hasMyPosition); + return sortingTypes; +} + +- (void)sortDefault +{ + auto & bm = GetFramework().GetBookmarkManager(); + bm.ResetLastSortingType(self->m_categoryId); + self->m_sortedBlocks.clear(); + [self calculateSections]; + [self.tableView reloadData]; +} + - (void)sort:(BookmarkManager::SortingType)type { bool hasMyPosition = false; @@ -755,100 +452,519 @@ using namespace std; bm.GetSortedBookmarks(sortParams); } -+ (NSString *)getLocalizedSortingType:(BookmarkManager::SortingType)type +- (BookmarkManager::SortedBlock &)sortedBlockForIndex:(NSNumber *)blockIndex { - switch (type) - { - case BookmarkManager::SortingType::ByTime: return L(@"sort_date"); - case BookmarkManager::SortingType::ByDistance: return L(@"sort_distance"); - case BookmarkManager::SortingType::ByType: return L(@"sort_type"); - } - UNREACHABLE(); + CHECK(blockIndex != nil, ()); + NSInteger index = blockIndex.integerValue; + CHECK_LESS(index, m_sortedBlocks.size(), ()); + return m_sortedBlocks[index]; } -- (std::vector)getAvailableSortingTypes +- (void)deleteSortedBlockForIndex:(NSNumber *)blockIndex { - CLLocation * lastLocation = [MWMLocationManager lastLocation]; - bool const hasMyPosition = lastLocation != nil; + CHECK(blockIndex != nil, ()); + NSInteger index = blockIndex.integerValue; + CHECK_LESS(index, m_sortedBlocks.size(), ()); + m_sortedBlocks.erase(m_sortedBlocks.begin() + index); +} + +- (UIActivityIndicatorView *)sortSpinner +{ + if (!_sortSpinner) + { + _sortSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _sortSpinner.hidesWhenStopped = YES; + } + return _sortSpinner; +} + +- (void)showSortSpinner:(BOOL)show +{ + if (show) + [self.sortSpinner startAnimating]; + else + [self.sortSpinner stopAnimating]; +} + +- (void)cancelSearch +{ + GetFramework().CancelSearch(search::Mode::Bookmarks); + m_searchResults.clear(); + + [self showNoResultsView:NO]; + [self showSpinner:NO]; + + [self calculateSections]; + [self.tableView reloadData]; +} + +- (MWMSearchNoResults *)noResultsView +{ + if (!_noResultsView) + { + _noResultsView = [MWMSearchNoResults viewWithImage:[UIImage imageNamed:@"img_search_not_found"] + title:L(@"search_not_found") + text:L(@"search_not_found_query")]; + } + return _noResultsView; +} + +- (void)showNoResultsView:(BOOL)show +{ + if (!show) + { + self.tableView.hidden = NO; + self.noResultsContainer.hidden = YES; + [self.noResultsView removeFromSuperview]; + } + else + { + self.tableView.hidden = YES; + self.noResultsContainer.hidden = NO; + [self.noResultsContainer addSubview:self.noResultsView]; + self.noResultsView.translatesAutoresizingMaskIntoConstraints = NO; + [NSLayoutConstraint activateConstraints:@[ + [self.noResultsView.topAnchor constraintEqualToAnchor:self.noResultsContainer.topAnchor], + [self.noResultsView.leftAnchor constraintEqualToAnchor:self.noResultsContainer.leftAnchor], + [self.noResultsView.bottomAnchor constraintEqualToAnchor:self.noResultsContainer.bottomAnchor], + [self.noResultsView.rightAnchor constraintEqualToAnchor:self.noResultsContainer.rightAnchor], + ]]; + [self onKeyboardAnimation]; + } +} + +- (UIActivityIndicatorView *)spinner +{ + if (!_spinner) + { + _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _spinner.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; + _spinner.hidesWhenStopped = YES; + } + return _spinner; +} + +- (UIImageView *)searchIcon +{ + if (!_searchIcon) + { + _searchIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_search"]]; + _searchIcon.mwm_coloring = MWMImageColoringBlack; + } + return _searchIcon; +} + +- (void)showSpinner:(BOOL)show +{ + UITextField * textField = [self.searchBar valueForKey:@"searchField"]; + if (!show) + { + textField.leftView = self.searchIcon; + [self.spinner stopAnimating]; + } + else + { + self.spinner.bounds = textField.leftView.bounds; + textField.leftView = self.spinner; + [self.spinner startAnimating]; + } +} + +- (NSString *)categoryFileName +{ + return @(GetFramework().GetBookmarkManager().GetCategoryFileName(m_categoryId).c_str()); +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + setStatusBarBackgroundColor(UIColor.clearColor); + return UIStatusBarStyleLightContent; +} + +- (void)setEditing:(BOOL)editing animated:(BOOL)animated +{ + [super setEditing:editing animated:animated]; + [self.tableView setEditing:editing animated:animated]; +} + +#pragma mark - UISearchBarDelegate + +- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar +{ + [self.searchBar setShowsCancelButton:YES animated:YES]; + [self.navigationController setNavigationBarHidden:YES animated:YES]; + self.tableView.contentInset = self.tableView.scrollIndicatorInsets = {}; + + // Allow to send all notifications in BM. + [[MWMBookmarksManager sharedManager] setNotificationsEnabled: YES]; + + return YES; +} + +- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar +{ + [self.searchBar setShowsCancelButton:NO animated:YES]; + [self.navigationController setNavigationBarHidden:NO animated:YES]; + self.tableView.contentInset = self.tableView.scrollIndicatorInsets = {}; + + // Disable all notifications in BM. + [[MWMBookmarksManager sharedManager] setNotificationsEnabled: NO]; + + return YES; +} + +- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar +{ + self.searchBar.text = @""; + [self.searchBar resignFirstResponder]; + [self cancelSearch]; +} + +- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText +{ + if (!searchText || searchText.length == 0) + { + [self cancelSearch]; + return; + } + + search::BookmarksSearchParams searchParams; + searchParams.m_query = searchText.UTF8String; + searchParams.m_groupId = m_categoryId; + + auto const searchId = ++self.lastSearchId; + __weak auto weakSelf = self; + searchParams.m_onStarted = [] {}; + searchParams.m_onResults = [weakSelf, searchId](search::BookmarksSearchParams::Results const & results, + search::BookmarksSearchParams::Status status) { + __strong auto self = weakSelf; + if (!self || searchId != self.lastSearchId) + return; + + auto const & bm = GetFramework().GetBookmarkManager(); + auto filteredResults = results; + bm.FilterInvalidBookmarks(filteredResults); + self->m_searchResults = filteredResults; + + if (status == search::BookmarksSearchParams::Status::Cancelled) + { + [self showSpinner:NO]; + } + else if (status == search::BookmarksSearchParams::Status::Completed) + { + [self showNoResultsView:results.empty()]; + [self showSpinner:NO]; + } + + [self calculateSections]; + [self.tableView reloadData]; + }; + + [self showSpinner:YES]; + + GetFramework().SearchInBookmarks(searchParams); +} + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return [m_sectionsCollection count]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return [m_sectionsCollection[section] numberOfRows]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section +{ + return [m_sectionsCollection[section] title]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell * cell = [m_sectionsCollection[indexPath.section] tableView:tableView cellForRow:indexPath.row]; + + cell.backgroundColor = [UIColor white]; + cell.textLabel.textColor = [UIColor blackPrimaryText]; + cell.detailTextLabel.textColor = [UIColor blackSecondaryText]; + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Remove cell selection + [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; + + auto const close = [m_sectionsCollection[indexPath.section] didSelectRow:indexPath.row]; + + [self.searchBar resignFirstResponder]; + + if (close) + [self.navigationController popToRootViewControllerAnimated:NO]; +} + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath +{ + return [m_sectionsCollection[indexPath.section] canEdit]; +} + +- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath +{ + self.editing = YES; +} + +- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath +{ + self.editing = NO; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (![m_sectionsCollection[indexPath.section] canEdit]) + return; + + BOOL emptySection = NO; + if (editingStyle == UITableViewCellEditingStyleDelete) + emptySection = [m_sectionsCollection[indexPath.section] deleteRow:indexPath.row]; + + [self calculateSections]; + + // We can delete the row with animation, if the sections stay the same. + if (!emptySection) + [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; + else + [self.tableView reloadData]; + auto const & bm = GetFramework().GetBookmarkManager(); - auto const sortingTypes = bm.GetAvailableSortingTypes(m_categoryId, hasMyPosition); - return sortingTypes; + if (bm.GetUserMarkIds(m_categoryId).size() + bm.GetTrackIds(m_categoryId).size() == 0) + { + self.navigationItem.rightBarButtonItem = nil; + [self setEditing:NO animated:YES]; + } } -- (IBAction)onSort:(UIBarButtonItem *)sender +- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { - auto actionSheet = [UIAlertController alertControllerWithTitle:nil - message:nil - preferredStyle:UIAlertControllerStyleActionSheet]; - - auto const sortingTypes = [self getAvailableSortingTypes]; - - for (auto type : sortingTypes) + auto header = (UITableViewHeaderFooterView *)view; + header.textLabel.textColor = [UIColor blackSecondaryText]; + header.textLabel.font = [UIFont medium14]; +} + +#pragma mark - BookmarksSectionDelegate + +- (NSInteger)numberOfBookmarksInSection:(BookmarksSection *)bookmarkSection +{ + if ([self isSearchMode]) { - [actionSheet addAction:[UIAlertAction actionWithTitle:[BookmarksVC getLocalizedSortingType:type] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) - { - auto & bm = GetFramework().GetBookmarkManager(); - bm.SetLastSortingType(self->m_categoryId, type); - [self sort:type]; - }]]; + return m_searchResults.size(); } - [actionSheet addAction:[UIAlertAction actionWithTitle:L(@"sort_default") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) - { - auto & bm = GetFramework().GetBookmarkManager(); - bm.ResetLastSortingType(self->m_categoryId); - self->m_sortedBlocks.clear(); - [self calculateSections]; - [self.tableView reloadData]; - }]]; + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:bookmarkSection.blockIndex]; + return sortedBlock.m_markIds.size(); + } + auto const & bm = GetFramework().GetBookmarkManager(); + return bm.GetUserMarkIds(m_categoryId).size(); +} + +- (NSString *)titleOfBookmarksSection:(BookmarksSection *)bookmarkSection +{ + if ([self isSearchMode]) + return nil; - [actionSheet addAction:[UIAlertAction actionWithTitle:L(@"cancel") - style:UIAlertActionStyleCancel - handler:nil]]; + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:bookmarkSection.blockIndex]; + return @(sortedBlock.m_blockName.c_str()); + } - actionSheet.popoverPresentationController.barButtonItem = self.sortItem; - [self presentViewController:actionSheet animated:YES completion:^{ - actionSheet.popoverPresentationController.passthroughViews = nil; - }]; + return L(@"bookmarks"); } -- (IBAction)onViewOnMap:(UIBarButtonItem *)sender +- (BOOL)canEditBookmarksSection:(BookmarksSection *)bookmarkSection { - [self viewOnMap]; + return [[MWMBookmarksManager sharedManager] isCategoryEditable:m_categoryId]; } -- (void)openCategorySettings +- (kml::MarkId)bookmarkSection:(BookmarksSection *)bookmarkSection getBookmarkIdByRow:(NSInteger)row { - auto storyboard = [UIStoryboard instance:MWMStoryboardCategorySettings]; - auto settingsController = (CategorySettingsViewController *)[storyboard instantiateInitialViewController]; - settingsController.delegate = self; - settingsController.category = [[MWMBookmarksManager sharedManager] categoryWithId:m_categoryId]; - [self.navigationController pushViewController:settingsController animated:YES]; + if ([self isSearchMode]) + { + CHECK_LESS(row, m_searchResults.size(), ()); + return m_searchResults[row]; + } + + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:bookmarkSection.blockIndex]; + CHECK_LESS(row, sortedBlock.m_markIds.size(), ()); + return sortedBlock.m_markIds[row]; + } + + auto const & bm = GetFramework().GetBookmarkManager(); + auto const & bookmarkIds = bm.GetUserMarkIds(m_categoryId); + CHECK_LESS(row, bookmarkIds.size(), ()); + auto it = bookmarkIds.begin(); + advance(it, row); + return *it; } -- (void)exportFile +- (BOOL)bookmarkSection:(BookmarksSection *)bookmarkSection onDeleteBookmarkInRow:(NSInteger)row { - [[MWMBookmarksManager sharedManager] addObserver:self]; - [[MWMBookmarksManager sharedManager] shareCategory:m_categoryId]; + if ([self isSearchMode]) + { + CHECK_LESS(row, m_searchResults.size(), ()); + m_searchResults.erase(m_searchResults.begin() + row); + m_sortedBlocks.clear(); + return m_searchResults.empty(); + } + + if ([self isSortMode]) + { + auto & sortedBlock = [self sortedBlockForIndex:bookmarkSection.blockIndex]; + auto & marks = sortedBlock.m_markIds; + CHECK_LESS(row, marks.size(), ()); + + marks.erase(marks.begin() + row); + if (marks.empty()) + { + [self deleteSortedBlockForIndex:bookmarkSection.blockIndex]; + return YES; + } + return NO; + } + + auto const & bm = GetFramework().GetBookmarkManager(); + auto const & bookmarkIds = bm.GetUserMarkIds(m_categoryId); + return bookmarkIds.empty(); } -- (void)shareCategory +#pragma mark - TracksSectionDelegate + +- (NSInteger)numberOfTracksInSection:(TracksSection *)tracksSection { - auto storyboard = [UIStoryboard instance:MWMStoryboardSharing]; - auto shareController = (BookmarksSharingViewController *)[storyboard instantiateInitialViewController]; - shareController.delegate = self; - shareController.category = [[MWMBookmarksManager sharedManager] categoryWithId:m_categoryId]; - [self.navigationController pushViewController:shareController animated:YES]; + CHECK(![self isSearchMode], ()); + + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:tracksSection.blockIndex]; + return sortedBlock.m_trackIds.size(); + } + + auto const & bm = GetFramework().GetBookmarkManager(); + return bm.GetTrackIds(m_categoryId).size(); } -- (void)viewOnMap +- (NSString *)titleOfTracksSection:(TracksSection *)tracksSection { - [self.navigationController popToRootViewControllerAnimated:YES]; - GetFramework().ShowBookmarkCategory(m_categoryId); + CHECK(![self isSearchMode], ()); + + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:tracksSection.blockIndex]; + return @(sortedBlock.m_blockName.c_str()); + } + + return L(@"tracks_title"); +} + +- (BOOL)canEditTracksSection:(TracksSection *)tracksSection +{ + CHECK(![self isSearchMode], ()); + + if ([self isSortMode]) + return false; + + return [[MWMBookmarksManager sharedManager] isCategoryEditable:m_categoryId]; +} + +- (kml::TrackId)tracksSection:(TracksSection *)tracksSection getTrackIdByRow:(NSInteger)row +{ + CHECK(![self isSearchMode], ()); + + if ([self isSortMode]) + { + auto const & sortedBlock = [self sortedBlockForIndex:tracksSection.blockIndex]; + CHECK_LESS(row, sortedBlock.m_trackIds.size(), ()); + return sortedBlock.m_trackIds[row]; + } + + auto const & bm = GetFramework().GetBookmarkManager(); + auto const & trackIds = bm.GetTrackIds(m_categoryId); + CHECK_LESS(row, trackIds.size(), ()); + auto it = trackIds.begin(); + advance(it, row); + return *it; +} + +- (BOOL)tracksSection:(TracksSection *)tracksSection onDeleteTrackInRow:(NSInteger)row +{ + CHECK(![self isSearchMode], ()); + + if ([self isSortMode]) + { + auto & sortedBlock = [self sortedBlockForIndex:tracksSection.blockIndex]; + CHECK_LESS(row, sortedBlock.m_trackIds.size(), ()); + + auto & tracks = sortedBlock.m_trackIds; + tracks.erase(tracks.begin() + row); + if (tracks.empty()) + { + [self deleteSortedBlockForIndex:tracksSection.blockIndex]; + return YES; + } + return NO; + } + + auto const & bm = GetFramework().GetBookmarkManager(); + auto const & trackIds = bm.GetTrackIds(m_categoryId); + return trackIds.empty(); +} + +#pragma mark - InfoSectionDelegate + +- (UITableViewCell *)infoCellForTableView: (UITableView *)tableView +{ + UITableViewCell * cell = [tableView dequeueReusableCellWithCellClass:MWMCategoryInfoCell.class]; + CHECK(cell, ("Invalid category info cell.")); + + auto & f = GetFramework(); + auto & bm = f.GetBookmarkManager(); + bool const categoryExists = bm.HasBmCategory(m_categoryId); + CHECK(categoryExists, ("Nonexistent category")); + + auto infoCell = (MWMCategoryInfoCell *)cell; + auto const & categoryData = bm.GetCategoryData(m_categoryId); + [infoCell updateWithCategoryData:categoryData delegate:self]; + infoCell.expanded = self.infoExpanded; + + return cell; +} + +#pragma mark - MWMCategoryInfoCellDelegate + +- (void)categoryInfoCellDidPressMore:(MWMCategoryInfoCell *)cell +{ + [self.tableView beginUpdates]; + cell.expanded = YES; + [self.tableView endUpdates]; + self.infoExpanded = YES; +} + +#pragma mark - MWMLocationObserver + +- (void)onLocationUpdate:(location::GpsInfo const &)info +{ + [self.tableView.visibleCells enumerateObjectsUsingBlock:^(UITableViewCell * cell, NSUInteger idx, BOOL * stop) + { + auto const indexPath = [self.tableView indexPathForCell:cell]; + auto const & section = self->m_sectionsCollection[indexPath.section]; + if ([section respondsToSelector:@selector(updateCell:forRow:withNewLocation:)]) + [section updateCell:cell forRow:indexPath.row withNewLocation:info]; + }]; } #pragma mark - MWMBookmarksObserver @@ -905,96 +1021,6 @@ using namespace std; [self.tableView reloadData]; } -#pragma mark - UISearchBarDelegate - -- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar -{ - [self.searchBar setShowsCancelButton:YES animated:YES]; - [self.navigationController setNavigationBarHidden:YES animated:YES]; - self.tableView.contentInset = self.tableView.scrollIndicatorInsets = {}; - - // Allow to send all notifications in BM. - [[MWMBookmarksManager sharedManager] setNotificationsEnabled: YES]; - - return YES; -} - -- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar -{ - [self.searchBar setShowsCancelButton:NO animated:YES]; - [self.navigationController setNavigationBarHidden:NO animated:YES]; - self.tableView.contentInset = self.tableView.scrollIndicatorInsets = {}; - - // Disable all notifications in BM. - [[MWMBookmarksManager sharedManager] setNotificationsEnabled: NO]; - - return YES; -} - -- (void)cancelSearch -{ - GetFramework().CancelSearch(search::Mode::Bookmarks); - m_searchResults.clear(); - - [self showNoResultsView:NO]; - [self showSpinner:NO]; - - [self calculateSections]; - [self.tableView reloadData]; -} - -- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar -{ - self.searchBar.text = @""; - [self.searchBar resignFirstResponder]; - [self cancelSearch]; -} - -- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText -{ - if (!searchText || searchText.length == 0) - { - [self cancelSearch]; - return; - } - - search::BookmarksSearchParams searchParams; - searchParams.m_query = searchText.UTF8String; - searchParams.m_groupId = m_categoryId; - - auto const searchId = ++self.lastSearchId; - __weak auto weakSelf = self; - searchParams.m_onStarted = [] {}; - searchParams.m_onResults = [weakSelf, searchId](search::BookmarksSearchParams::Results const & results, - search::BookmarksSearchParams::Status status) { - __strong auto self = weakSelf; - if (!self || searchId != self.lastSearchId) - return; - - auto const & bm = GetFramework().GetBookmarkManager(); - auto filteredResults = results; - bm.FilterInvalidBookmarks(filteredResults); - self->m_searchResults = filteredResults; - - if (status == search::BookmarksSearchParams::Status::Cancelled) - { - [self showSpinner:NO]; - } - else if (status == search::BookmarksSearchParams::Status::Completed) - { - [self showNoResultsView:results.empty()]; - [self showSpinner:NO]; - } - - [self calculateSections]; - [self.tableView reloadData]; - }; - - [self showSpinner:YES]; - - GetFramework().SearchInBookmarks(searchParams); -} - #pragma mark - MWMKeyboard - (void)onKeyboardAnimation diff --git a/iphone/Maps/Bookmarks/InfoSection.h b/iphone/Maps/Bookmarks/InfoSection.h new file mode 100644 index 0000000000..ec0e1fb209 --- /dev/null +++ b/iphone/Maps/Bookmarks/InfoSection.h @@ -0,0 +1,14 @@ +#import "TableSectionDataSource.h" + +@protocol InfoSectionDelegate + +- (UITableViewCell *)infoCellForTableView:(UITableView *)tableView; + +@end + +@interface InfoSection : NSObject + +- (instancetype)initWithDelegate:(id)delegate; + +@end + diff --git a/iphone/Maps/Bookmarks/InfoSection.mm b/iphone/Maps/Bookmarks/InfoSection.mm new file mode 100644 index 0000000000..5fadb7dcd0 --- /dev/null +++ b/iphone/Maps/Bookmarks/InfoSection.mm @@ -0,0 +1,51 @@ +#import "InfoSection.h" + +@interface InfoSection() + +@property (weak, nonatomic) id delegate; + +@end + +@implementation InfoSection + +- (instancetype)initWithDelegate: (id)delegate +{ + self = [super init]; + if (self) + { + _delegate = delegate; + } + return self; +} + +- (NSInteger)numberOfRows +{ + return 1; +} + +- (NSString *)title +{ + return L(@"placepage_place_description"); +} + +- (BOOL)canEdit +{ + return NO; +} + +- (UITableViewCell *)tableView: (UITableView *)tableView cellForRow: (NSInteger)row +{ + return [self.delegate infoCellForTableView:tableView]; +} + +- (BOOL)didSelectRow: (NSInteger)row +{ + return NO; +} + +- (BOOL)deleteRow: (NSInteger)row +{ + return YES; +} + +@end diff --git a/iphone/Maps/Bookmarks/MWMCategoryInfoCell.h b/iphone/Maps/Bookmarks/MWMCategoryInfoCell.h index 66255e4348..c09cffeb72 100644 --- a/iphone/Maps/Bookmarks/MWMCategoryInfoCell.h +++ b/iphone/Maps/Bookmarks/MWMCategoryInfoCell.h @@ -1,5 +1,5 @@ #import "MWMTableViewCell.h" -#import "BookmarksSection.h" +#import "InfoSection.h" namespace kml { diff --git a/iphone/Maps/Bookmarks/TableSectionDataSource.h b/iphone/Maps/Bookmarks/TableSectionDataSource.h new file mode 100644 index 0000000000..679179fba4 --- /dev/null +++ b/iphone/Maps/Bookmarks/TableSectionDataSource.h @@ -0,0 +1,21 @@ +#include "platform/location.hpp" + +@protocol TableSectionDataSource + +- (NSInteger)numberOfRows; +- (NSString *)title; +- (BOOL)canEdit; + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRow:(NSInteger)row; + +- (BOOL)didSelectRow:(NSInteger)row; +- (BOOL)deleteRow:(NSInteger)row; + +@optional + +-(void)updateCell:(UITableViewCell *)cell + forRow:(NSInteger)row + withNewLocation:(location::GpsInfo const &)gpsInfo; + +@end diff --git a/iphone/Maps/Bookmarks/TracksSection.h b/iphone/Maps/Bookmarks/TracksSection.h new file mode 100644 index 0000000000..d00fffe31e --- /dev/null +++ b/iphone/Maps/Bookmarks/TracksSection.h @@ -0,0 +1,26 @@ +#import "TableSectionDataSource.h" + +#include "kml/type_utils.hpp" + +@class TracksSection; + +@protocol TracksSectionDelegate + +- (NSInteger)numberOfTracksInSection:(TracksSection *)tracksSection; +- (NSString *)titleOfTracksSection:(TracksSection *)tracksSection; +- (BOOL)canEditTracksSection:(TracksSection *)tracksSection; +- (kml::MarkId)tracksSection:(TracksSection *)tracksSection getTrackIdByRow:(NSInteger)row; +- (BOOL)tracksSection:(TracksSection *)tracksSection onDeleteTrackInRow:(NSInteger)row; + +@end + +@interface TracksSection : NSObject + +@property (nullable, nonatomic) NSNumber * blockIndex; + +- (instancetype)initWithDelegate:(id)delegate; + +- (instancetype)initWithBlockIndex:(NSNumber *)blockIndex + delegate:(id)delegate; + +@end diff --git a/iphone/Maps/Bookmarks/TracksSection.mm b/iphone/Maps/Bookmarks/TracksSection.mm new file mode 100644 index 0000000000..a32b8119a6 --- /dev/null +++ b/iphone/Maps/Bookmarks/TracksSection.mm @@ -0,0 +1,90 @@ +#import "TracksSection.h" +#import "CircleView.h" +#include "Framework.h" + +namespace +{ + CGFloat const kPinDiameter = 22.0f; +} // namespace + +@interface TracksSection() + +@property (weak, nonatomic) id delegate; + +@end + +@implementation TracksSection + +- (instancetype)initWithDelegate:(id)delegate +{ + return [self initWithBlockIndex:nil delegate:delegate]; +} + +- (instancetype)initWithBlockIndex:(NSNumber *)blockIndex delegate: (id)delegate +{ + self = [super init]; + if (self) + { + _blockIndex = blockIndex; + _delegate = delegate; + } + return self; +} + +- (NSInteger)numberOfRows +{ + return [self.delegate numberOfTracksInSection:self]; +} + +- (NSString *)title +{ + return [self.delegate titleOfTracksSection:self]; +} + +- (BOOL)canEdit +{ + return [self.delegate canEditTracksSection:self]; +} + +- (UITableViewCell *)tableView: (UITableView *)tableView cellForRow: (NSInteger)row +{ + UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"TrackCell"]; + if (!cell) + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"TrackCell"]; + CHECK(cell, ("Invalid track cell.")); + + auto const & bm = GetFramework().GetBookmarkManager(); + + kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; + Track const * track = bm.GetTrack(trackId); + cell.textLabel.text = @(track->GetName().c_str()); + string dist; + if (measurement_utils::FormatDistance(track->GetLengthMeters(), dist)) + cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", L(@"length"), @(dist.c_str())]; + else + cell.detailTextLabel.text = nil; + dp::Color const c = track->GetColor(0); + cell.imageView.image = [CircleView createCircleImageWith:kPinDiameter + andColor:[UIColor colorWithRed:c.GetRedF() + green:c.GetGreenF() + blue:c.GetBlueF() + alpha:1.f]]; + return cell; +} + +- (BOOL)didSelectRow: (NSInteger)row +{ + kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; + GetFramework().ShowTrack(trackId); + return YES; +} + +- (BOOL)deleteRow: (NSInteger)row +{ + kml::TrackId const trackId = [self.delegate tracksSection:self getTrackIdByRow:row]; + auto & bm = GetFramework().GetBookmarkManager(); + bm.GetEditSession().DeleteTrack(trackId); + return [self.delegate tracksSection:self onDeleteTrackInRow:row]; +} + +@end diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 35b3a6eaef..4070bb3716 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -589,6 +589,8 @@ BB8123CF212C264700ADE512 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB8123CD212C264700ADE512 /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; BB8123D0212C264700ADE512 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB8123CE212C264700ADE512 /* MetalKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; BB8123D62130427E00ADE512 /* MetalContextFactory.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB8123D52130427E00ADE512 /* MetalContextFactory.mm */; }; + BB87BF8A22FAF1CA008A8A72 /* TracksSection.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB87BF8922FAF1CA008A8A72 /* TracksSection.mm */; }; + BB87BF8D22FAF435008A8A72 /* InfoSection.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB87BF8C22FAF435008A8A72 /* InfoSection.mm */; }; BBED27022292F6C000788143 /* BookmarksSection.mm in Sources */ = {isa = PBXBuildFile; fileRef = BBED27012292F6C000788143 /* BookmarksSection.mm */; }; CD08887422B7ABB400C1368D /* MWMDiscoveryCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD08887322B7ABB400C1368D /* MWMDiscoveryCollectionView.mm */; }; CD4A1F132305872700F2A6B6 /* PromoBookingPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4A1F122305872700F2A6B6 /* PromoBookingPresentationController.swift */; }; @@ -1679,6 +1681,11 @@ BB8123CE212C264700ADE512 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; BB8123D42130427E00ADE512 /* MetalContextFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalContextFactory.h; sourceTree = ""; }; BB8123D52130427E00ADE512 /* MetalContextFactory.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MetalContextFactory.mm; sourceTree = ""; }; + BB87BF8722FAEC82008A8A72 /* TableSectionDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableSectionDataSource.h; sourceTree = ""; }; + BB87BF8822FAF125008A8A72 /* TracksSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TracksSection.h; sourceTree = ""; }; + BB87BF8922FAF1CA008A8A72 /* TracksSection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TracksSection.mm; sourceTree = ""; }; + BB87BF8B22FAF3B8008A8A72 /* InfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoSection.h; sourceTree = ""; }; + BB87BF8C22FAF435008A8A72 /* InfoSection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InfoSection.mm; sourceTree = ""; }; BBED27002292F42000788143 /* BookmarksSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BookmarksSection.h; sourceTree = ""; }; BBED27012292F6C000788143 /* BookmarksSection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BookmarksSection.mm; sourceTree = ""; }; CD08887222B7ABB400C1368D /* MWMDiscoveryCollectionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryCollectionView.h; sourceTree = ""; }; @@ -4735,6 +4742,11 @@ B33D21B720E130D000BAD749 /* BookmarksTabViewController.swift */, BBED27002292F42000788143 /* BookmarksSection.h */, BBED27012292F6C000788143 /* BookmarksSection.mm */, + BB87BF8722FAEC82008A8A72 /* TableSectionDataSource.h */, + BB87BF8822FAF125008A8A72 /* TracksSection.h */, + BB87BF8922FAF1CA008A8A72 /* TracksSection.mm */, + BB87BF8B22FAF3B8008A8A72 /* InfoSection.h */, + BB87BF8C22FAF435008A8A72 /* InfoSection.mm */, ); path = Bookmarks; sourceTree = ""; @@ -5420,6 +5432,7 @@ CDCA2745223FCFD200167D87 /* SearchResultInfo.swift in Sources */, 349A13831DEC138C00C7DB60 /* MWMMobileInternetAlert.mm in Sources */, 349D1ADE1E2E325C004A2006 /* MWMBottomMenuViewController.mm in Sources */, + BB87BF8A22FAF1CA008A8A72 /* TracksSection.mm in Sources */, 6741A9EC1BF340DE002C974C /* MWMCircularProgress.mm in Sources */, 470A89FD21342A9D00D72FBF /* TutorialBlurView.swift in Sources */, 342CC5F21C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm in Sources */, @@ -5454,6 +5467,7 @@ 3358607E217632A2006D11F2 /* BookmarksSharingViewController.swift in Sources */, CDB92CF1229EB8A800EC757C /* MWMDiscoverySearchViewModel.m in Sources */, 34AB662F1FC5AA330078E451 /* RouteManageriPhonePresentationController.swift in Sources */, + BB87BF8D22FAF435008A8A72 /* InfoSection.mm in Sources */, 4797A4DC226F4B2900D3A984 /* DeepLinkHandler.swift in Sources */, 34AB66201FC5AA330078E451 /* RouteStartButton.swift in Sources */, 34F4072C1E9E1AFF00E57AC0 /* Banner.swift in Sources */,