mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-21 02:33:50 +00:00
[ios] refactor search modes switching
For the expanded iphone mode (in portait) only the searchEverywhere will be used For the halfscreen everywhere + viewport For the iPad the everywhere + viewport is always enabled For the hidden only the viewport Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
committed by
Konstantin Pastbin
parent
6d093a45cf
commit
4cf5ec3c86
@@ -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<MWMSearchObserver>)observer;
|
||||
+ (void)removeObserver:(id<MWMSearchObserver>)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<SearchResult *> *)getResults;
|
||||
|
||||
|
||||
@@ -17,15 +17,11 @@ using Observers = NSHashTable<Observer>;
|
||||
@interface MWMSearch () <MWMFrameworkDrapeObserver>
|
||||
|
||||
@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<Observer>;
|
||||
}
|
||||
|
||||
- (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<Observer>;
|
||||
manager->m_isCategory = (query.source == SearchTextSourceCategory);
|
||||
manager.textChanged = YES;
|
||||
|
||||
[manager reset];
|
||||
[manager update];
|
||||
}
|
||||
|
||||
@@ -166,23 +173,6 @@ using Observers = NSHashTable<Observer>;
|
||||
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<SearchResult *> *)getResults {
|
||||
NSMutableArray<SearchResult *> * results = [[NSMutableArray alloc] initWithCapacity:MWMSearch.resultsCount];
|
||||
for (NSUInteger i = 0; i < MWMSearch.resultsCount; ++i) {
|
||||
@@ -219,9 +209,22 @@ using Observers = NSHashTable<Observer>;
|
||||
[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<Observer>;
|
||||
- (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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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?()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user