diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index ca88375ec..f050a1255 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -11,10 +11,17 @@ typedef NS_ENUM(NSUInteger, SearchTextSource) { SearchTextSourceDeeplink }; +typedef NS_ENUM(NSUInteger, SearchMode) { + SearchModeEverywhere, + SearchModeViewport, + SearchModeEverywhereAndViewport +}; + @class SearchResult; @class SearchQuery; @protocol SearchManager + + (void)addObserver:(id)observer; + (void)removeObserver:(id)observer; @@ -22,8 +29,8 @@ typedef NS_ENUM(NSUInteger, SearchTextSource) { + (void)searchQuery:(SearchQuery *)query; + (void)showResultAtIndex:(NSUInteger)index; -+ (void)showEverywhereSearchResultsOnMap; -+ (void)showViewportSearchResultsOnMap; ++ (SearchMode)searchMode; ++ (void)setSearchMode:(SearchMode)mode; + (NSArray *)getResults; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index d26ced3a9..60cc81eb3 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -17,15 +17,11 @@ using Observers = NSHashTable; @interface MWMSearch () @property(nonatomic) NSUInteger suggestionsCount; - +@property(nonatomic) SearchMode searchMode; @property(nonatomic) BOOL textChanged; - -@property(nonatomic) Observers *observers; - +@property(nonatomic) Observers * observers; @property(nonatomic) NSUInteger lastSearchTimestamp; - -@property(nonatomic) SearchIndex *itemsIndex; - +@property(nonatomic) SearchIndex * itemsIndex; @property(nonatomic) NSInteger searchCount; @end @@ -106,11 +102,21 @@ using Observers = NSHashTable; } - (void)update { - [self reset]; if (m_query.empty()) return; - [self searchInViewport]; - [self searchEverywhere]; + + switch (self.searchMode) { + case SearchModeEverywhere: + [self searchEverywhere]; + break; + case SearchModeViewport: + [self searchInViewport]; + break; + case SearchModeEverywhereAndViewport: + [self searchEverywhere]; + [self searchInViewport]; + break; + } } #pragma mark - Add/Remove Observers @@ -150,6 +156,7 @@ using Observers = NSHashTable; manager->m_isCategory = (query.source == SearchTextSourceCategory); manager.textChanged = YES; + [manager reset]; [manager update]; } @@ -166,23 +173,6 @@ using Observers = NSHashTable; return result; } -+ (void)showEverywhereSearchResultsOnMap { - MWMSearch * manager = [MWMSearch manager]; - if (![MWMRouter isRoutingActive]) { - auto const & results = manager->m_everywhereResults; - if (results.GetCount() == 1) - [self showResultAtIndex:0]; - else - GetFramework().ShowSearchResults(manager->m_everywhereResults); - } -} - -+ (void)showViewportSearchResultsOnMap { - MWMSearch * manager = [MWMSearch manager]; - if (![MWMRouter isRoutingActive]) - [manager processViewportChangedEvent]; -} - + (NSArray *)getResults { NSMutableArray * results = [[NSMutableArray alloc] initWithCapacity:MWMSearch.resultsCount]; for (NSUInteger i = 0; i < MWMSearch.resultsCount; ++i) { @@ -219,9 +209,22 @@ using Observers = NSHashTable; [manager reset]; } ++ (SearchMode)searchMode { + return [MWMSearch manager].searchMode; +} + ++ (void)setSearchMode:(SearchMode)mode { + MWMSearch * manager = [MWMSearch manager]; + if (manager.searchMode == mode) + return; + manager.searchMode = mode; + [manager update]; +} + + (NSUInteger)suggestionsCount { return [MWMSearch manager].suggestionsCount; } + + (NSUInteger)resultsCount { return [MWMSearch manager].itemsIndex.count; } @@ -264,7 +267,19 @@ using Observers = NSHashTable; - (void)processViewportChangedEvent { if (!GetFramework().GetSearchAPI().IsViewportSearchActive()) return; - [self searchEverywhere]; + + BOOL const isSearchCompleted = self.searchCount == 0; + if (!isSearchCompleted) + return; + + switch (self.searchMode) { + case SearchModeEverywhere: + case SearchModeViewport: + break; + case SearchModeEverywhereAndViewport: + [self searchEverywhere]; + break; + } } #pragma mark - Properties diff --git a/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift b/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift index 33c96f3b6..c670151e8 100644 --- a/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift +++ b/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift @@ -239,6 +239,23 @@ final class SearchOnMapTests: XCTestCase { XCTAssertEqual(view.viewModel.presentationStep, .halfScreen) XCTAssertEqual(view.viewModel.isTyping, false) // No typing when deeplink is used } + + func test_GivenSearchIsActive_WhenPresentationStepUpdate_ThenUpdateSearchMode() { + interactor.handle(.openSearch) + XCTAssertEqual(searchManager.searchMode(), isiPad ? .everywhereAndViewport : .everywhere) + + interactor.handle(.didUpdatePresentationStep(.halfScreen)) + XCTAssertEqual(searchManager.searchMode(), .everywhereAndViewport) + + interactor.handle(.didUpdatePresentationStep(.compact)) + XCTAssertEqual(searchManager.searchMode(), .everywhereAndViewport) + + interactor.handle(.didUpdatePresentationStep(.hidden)) + XCTAssertEqual(searchManager.searchMode(), .viewport) + + interactor.handle(.didUpdatePresentationStep(.fullScreen)) + XCTAssertEqual(searchManager.searchMode(), isiPad ? .everywhereAndViewport : .everywhere) + } } // MARK: - Mocks @@ -264,6 +281,7 @@ private class SearchManagerMock: SearchManager { } } } + private static var _searchMode: SearchMode = .everywhere static func add(_ observer: any MWMSearchObserver) { self.observers.addListener(observer) @@ -276,10 +294,10 @@ private class SearchManagerMock: SearchManager { static func save(_ query: SearchQuery) {} static func searchQuery(_ query: SearchQuery) {} static func showResult(at index: UInt) {} - static func showEverywhereSearchResultsOnMap() {} - static func showViewportSearchResultsOnMap() {} static func clear() {} static func getResults() -> [SearchResult] { results.results } + static func searchMode() -> SearchMode { _searchMode } + static func setSearchMode(_ mode: SearchMode) { _searchMode = mode } } private extension SearchResult { diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalPresentationStepsController.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalPresentationStepsController.swift index f0c74c35e..38cd93bd1 100644 --- a/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalPresentationStepsController.swift +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalPresentationStepsController.swift @@ -77,7 +77,7 @@ final class ModalPresentationStepsController { } let animation: PresentationStepChangeAnimation = abs(velocity.y) > Constants.slowSwipeVelocity ? .slideAndBounce : .slide - setStep(nextStep, animation: animation, notifyAboutStepUpdate: true) + setStep(nextStep, animation: animation) default: break } @@ -86,27 +86,23 @@ final class ModalPresentationStepsController { func setStep(_ step: ModalPresentationStep, completion: (() -> Void)? = nil) { guard currentStep != step else { return } - setStep(step, animation: .slide, notifyAboutStepUpdate: false, completion: completion) + setStep(step, animation: .slide, completion: completion) } private func setStep(_ step: ModalPresentationStep, animation: PresentationStepChangeAnimation, - notifyAboutStepUpdate: Bool = true, completion: (() -> Void)? = nil) { guard let presentedView else { return } currentStep = step updateMaxAvailableFrame() let frame = frame(for: step) + didUpdateHandler?(.didUpdateStep(step)) didUpdateHandler?(.didUpdateFrame(frame)) ModalPresentationAnimator.animate(with: animation) { presentedView.frame = frame - } completion: { [weak self] _ in - guard let self else { return } - if notifyAboutStepUpdate { - self.didUpdateHandler?(.didUpdateStep(step)) - } + } completion: { _ in completion?() } } diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift index 198f5d5dd..254d919a9 100644 --- a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift @@ -4,7 +4,6 @@ final class SearchOnMapInteractor: NSObject { private let searchManager: SearchManager.Type private let routeManager: MWMRouter.Type private var isUpdatesDisabled = false - private var showResultsOnMap: Bool = false var routingTooltipSearch: SearchOnMapRoutingTooltipSearch = .none @@ -31,30 +30,44 @@ final class SearchOnMapInteractor: NSObject { switch event { case .openSearch: return .showHistoryAndCategory + case .hideSearch: return .setSearchScreenHidden(true) + case .didStartDraggingSearch: return .setIsTyping(false) + case .didStartTyping: return .setIsTyping(true) + case .didType(let searchText): return processTypedText(searchText) + case .clearButtonDidTap: return processClearButtonDidTap() + case .didSelect(let searchText): return processSelectedText(searchText) + case .searchButtonDidTap(let searchText): return processSearchButtonDidTap(searchText) + case .didSelectResult(let result, let query): return processSelectedResult(result, query: query) + case .didSelectPlaceOnMap: return isiPad ? .none : .setSearchScreenHidden(true) + case .didDeselectPlaceOnMap: return deselectPlaceOnMap() + case .didStartDraggingMap: return .setSearchScreenCompact + case .didUpdatePresentationStep(let step): + searchManager.setSearchMode(searchModeForPresentationStep(step)) return .updatePresentationStep(step) + case .closeSearch: return closeSearch() } @@ -68,8 +81,6 @@ final class SearchOnMapInteractor: NSObject { private func processSearchButtonDidTap(_ query: SearchQuery) -> SearchOnMap.Response { searchManager.save(query) - showResultsOnMap = true - searchManager.showEverywhereSearchResultsOnMap() return .showOnTheMap } @@ -85,7 +96,6 @@ final class SearchOnMapInteractor: NSObject { searchManager.save(query) } searchManager.searchQuery(query) - showResultsOnMap = true return .selectQuery(query) } @@ -127,33 +137,38 @@ final class SearchOnMapInteractor: NSObject { private func deselectPlaceOnMap() -> SearchOnMap.Response { routingTooltipSearch = .none - searchManager.showViewportSearchResultsOnMap() return .setSearchScreenHidden(false) } private func closeSearch() -> SearchOnMap.Response { routingTooltipSearch = .none isUpdatesDisabled = true - showResultsOnMap = false searchManager.clear() return .close } + + private func searchModeForPresentationStep(_ step: ModalPresentationStep) -> SearchMode { + switch step { + case .fullScreen: + return isiPad ? .everywhereAndViewport : .everywhere + case .halfScreen, .compact: + return .everywhereAndViewport + case .hidden: + return .viewport + } + } } // MARK: - MWMSearchObserver extension SearchOnMapInteractor: MWMSearchObserver { func onSearchCompleted() { - guard !isUpdatesDisabled else { return } + guard !isUpdatesDisabled, searchManager.searchMode() != .viewport else { return } let results = searchManager.getResults() - if showResultsOnMap && !results.isEmpty { - searchManager.showEverywhereSearchResultsOnMap() - showResultsOnMap = false - } presenter.process(.showResults(SearchOnMap.SearchResults(results), isSearchCompleted: true)) } func onSearchResultsUpdated() { - guard !isUpdatesDisabled else { return } + guard !isUpdatesDisabled, searchManager.searchMode() != .viewport else { return } let results = searchManager.getResults() guard !results.isEmpty else { return } presenter.process(.showResults(SearchOnMap.SearchResults(results), isSearchCompleted: false))