mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
[ios] refactor search - use SearchQuery class instead of text+locale+isCategory
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
committed by
Konstantin Pastbin
parent
09171651ff
commit
96c24cf973
@@ -2,6 +2,8 @@
|
|||||||
#import "MWMCarPlaySearchResultObject.h"
|
#import "MWMCarPlaySearchResultObject.h"
|
||||||
#import "MWMSearch.h"
|
#import "MWMSearch.h"
|
||||||
|
|
||||||
|
#import "SwiftBridge.h"
|
||||||
|
|
||||||
API_AVAILABLE(ios(12.0))
|
API_AVAILABLE(ios(12.0))
|
||||||
@interface MWMCarPlaySearchService ()<MWMSearchObserver>
|
@interface MWMCarPlaySearchService ()<MWMSearchObserver>
|
||||||
@property(strong, nonatomic, nullable) void (^completionHandler)(NSArray<MWMCarPlaySearchResultObject *> *searchResults);
|
@property(strong, nonatomic, nullable) void (^completionHandler)(NSArray<MWMCarPlaySearchResultObject *> *searchResults);
|
||||||
@@ -30,12 +32,14 @@ API_AVAILABLE(ios(12.0))
|
|||||||
self.lastResults = @[];
|
self.lastResults = @[];
|
||||||
self.completionHandler = completionHandler;
|
self.completionHandler = completionHandler;
|
||||||
/// @todo Didn't find pure category request in CarPlay.
|
/// @todo Didn't find pure category request in CarPlay.
|
||||||
[MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:NO];
|
SearchQuery * query = [[SearchQuery alloc] init:text locale:inputLocale source:SearchTextSourceTypedText];
|
||||||
|
[MWMSearch searchQuery:query];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)saveLastQuery {
|
- (void)saveLastQuery {
|
||||||
if (self.lastQuery != nil && self.inputLocale != nil) {
|
if (self.lastQuery != nil && self.inputLocale != nil) {
|
||||||
[MWMSearch saveQuery:self.lastQuery forInputLocale:self.inputLocale];
|
SearchQuery * query = [[SearchQuery alloc] init:self.lastQuery locale:self.inputLocale source:SearchTextSourceTypedText];
|
||||||
|
[MWMSearch saveQuery:query];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
@class MapViewController;
|
@class MapViewController;
|
||||||
@class BottomTabBarViewController;
|
@class BottomTabBarViewController;
|
||||||
@class TrackRecordingViewController;
|
@class TrackRecordingViewController;
|
||||||
|
@class SearchQuery;
|
||||||
|
|
||||||
@protocol MWMFeatureHolder;
|
@protocol MWMFeatureHolder;
|
||||||
|
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
#pragma mark - MWMSearchManager
|
#pragma mark - MWMSearchManager
|
||||||
|
|
||||||
- (void)actionDownloadMaps:(MWMMapDownloaderMode)mode;
|
- (void)actionDownloadMaps:(MWMMapDownloaderMode)mode;
|
||||||
- (BOOL)searchText:(NSString *)text forInputLocale:(NSString *)locale;
|
- (BOOL)search:(SearchQuery *)query;
|
||||||
- (void)searchTextOnMap:(NSString *)text forInputLocale:(NSString *)locale;
|
- (void)searchOnMap:(SearchQuery *)query;
|
||||||
|
|
||||||
#pragma mark - MWMFeatureHolder
|
#pragma mark - MWMFeatureHolder
|
||||||
|
|
||||||
|
|||||||
@@ -104,19 +104,19 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
|
|||||||
|
|
||||||
#pragma mark - MWMPlacePageViewManager
|
#pragma mark - MWMPlacePageViewManager
|
||||||
|
|
||||||
- (void)searchTextOnMap:(NSString *)text forInputLocale:(NSString *)locale {
|
- (void)searchOnMap:(SearchQuery *)query {
|
||||||
if (![self searchText:text forInputLocale:locale])
|
if (![self search:query])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self.searchManager startSearchingWithIsRouting:NO];
|
[self.searchManager startSearchingWithIsRouting:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)searchText:(NSString *)text forInputLocale:(NSString *)locale {
|
- (BOOL)search:(SearchQuery *)query {
|
||||||
if (text.length == 0)
|
if (query.text.length == 0)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self.searchManager startSearchingWithIsRouting:NO];
|
[self.searchManager startSearchingWithIsRouting:NO];
|
||||||
[self.searchManager searchText:text locale:locale isCategory:NO];
|
[self.searchManager searchText:query];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,10 +193,11 @@ BOOL defaultOrientation(CGSize const &size) {
|
|||||||
|
|
||||||
- (IBAction)searchButtonTouchUpInside:(MWMButton *)sender {
|
- (IBAction)searchButtonTouchUpInside:(MWMButton *)sender {
|
||||||
auto const body = ^(NavigationSearchState state) {
|
auto const body = ^(NavigationSearchState state) {
|
||||||
NSString *query = [kSearchButtonRequest.at(state) stringByAppendingString:@" "];
|
NSString * text = [kSearchButtonRequest.at(state) stringByAppendingString:@" "];
|
||||||
NSString *locale = [[AppInfo sharedInfo] languageId];
|
NSString * locale = [[AppInfo sharedInfo] languageId];
|
||||||
// Category request from navigation search wheel.
|
// Category request from navigation search wheel.
|
||||||
[MWMSearch searchQuery:query forInputLocale:locale withCategory:YES];
|
SearchQuery * query = [[SearchQuery alloc] init:text locale:locale source:SearchTextSourceCategory];
|
||||||
|
[MWMSearch searchQuery:query];
|
||||||
[self setSearchState:state animated:YES];
|
[self setSearchState:state animated:YES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
- (void)openEditor;
|
- (void)openEditor;
|
||||||
- (void)openBookmarkEditor;
|
- (void)openBookmarkEditor;
|
||||||
- (void)openFullPlaceDescriptionWithHtml:(NSString *_Nonnull)htmlString;
|
- (void)openFullPlaceDescriptionWithHtml:(NSString *_Nonnull)htmlString;
|
||||||
- (void)searchText:(NSString *_Nonnull)text;
|
|
||||||
- (void)openDrivingOptions;
|
- (void)openDrivingOptions;
|
||||||
|
|
||||||
- (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration;
|
- (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration;
|
||||||
|
|||||||
@@ -612,10 +612,6 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
[self.navigationController pushViewController:descriptionViewController animated:YES];
|
[self.navigationController pushViewController:descriptionViewController animated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)searchText:(NSString *)text {
|
|
||||||
[self.controlsManager searchText:text forInputLocale:[[AppInfo sharedInfo] languageId]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)openDrivingOptions {
|
- (void)openDrivingOptions {
|
||||||
UIStoryboard *sb = [UIStoryboard instance:MWMStoryboardDrivingOptions];
|
UIStoryboard *sb = [UIStoryboard instance:MWMStoryboardDrivingOptions];
|
||||||
UIViewController *vc = [sb instantiateInitialViewController];
|
UIViewController *vc = [sb instantiateInitialViewController];
|
||||||
|
|||||||
@@ -85,9 +85,10 @@ using namespace osm_auth_ios;
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SearchQuery * query = [[SearchQuery alloc] init:[searchString stringByAppendingString:@" "]
|
||||||
[[MWMMapViewControlsManager manager] searchText:[searchString stringByAppendingString:@" "]
|
locale:[MWMSettings spotlightLocaleLanguageId]
|
||||||
forInputLocale:[MWMSettings spotlightLocaleLanguageId]];
|
source:SearchTextSourceDeeplink];
|
||||||
|
[[MWMMapViewControlsManager manager] search:query];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)commonInit {
|
- (void)commonInit {
|
||||||
|
|||||||
@@ -113,10 +113,11 @@
|
|||||||
sd.onViewportChanged(kSearchInViewportZoom)
|
sd.onViewportChanged(kSearchInViewportZoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let searchQuery = SearchQuery(sd.query, locale: sd.locale, source: .deeplink)
|
||||||
if (sd.isSearchOnMap) {
|
if (sd.isSearchOnMap) {
|
||||||
MWMMapViewControlsManager.manager()?.searchText(onMap: sd.query, forInputLocale: sd.locale)
|
MWMMapViewControlsManager.manager()?.search(onMap: searchQuery)
|
||||||
} else {
|
} else {
|
||||||
MWMMapViewControlsManager.manager()?.searchText(sd.query, forInputLocale: sd.locale)
|
MWMMapViewControlsManager.manager()?.search(searchQuery)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
case .menu:
|
case .menu:
|
||||||
|
|||||||
@@ -3,14 +3,22 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, SearchTextSource) {
|
||||||
|
SearchTextSourceTypedText,
|
||||||
|
SearchTextSourceCategory,
|
||||||
|
SearchTextSourceSuggestion,
|
||||||
|
SearchTextSourceDeeplink
|
||||||
|
};
|
||||||
|
|
||||||
@class SearchResult;
|
@class SearchResult;
|
||||||
|
@class SearchQuery;
|
||||||
|
|
||||||
@protocol SearchManager
|
@protocol SearchManager
|
||||||
+ (void)addObserver:(id<MWMSearchObserver>)observer;
|
+ (void)addObserver:(id<MWMSearchObserver>)observer;
|
||||||
+ (void)removeObserver:(id<MWMSearchObserver>)observer;
|
+ (void)removeObserver:(id<MWMSearchObserver>)observer;
|
||||||
|
|
||||||
+ (void)saveQuery:(NSString *)query forInputLocale:(NSString *)inputLocale;
|
+ (void)saveQuery:(SearchQuery *)query;
|
||||||
+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory;
|
+ (void)searchQuery:(SearchQuery *)query;
|
||||||
|
|
||||||
+ (void)showResultAtIndex:(NSUInteger)index;
|
+ (void)showResultAtIndex:(NSUInteger)index;
|
||||||
+ (void)showEverywhereSearchResultsOnMap;
|
+ (void)showEverywhereSearchResultsOnMap;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ using Observers = NSHashTable<Observer>;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)searchInViewport {
|
- (void)searchInViewport {
|
||||||
search::ViewportSearchParams params{
|
search::ViewportSearchParams params {
|
||||||
m_query, m_locale, {} /* default timeout */, m_isCategory,
|
m_query, m_locale, {} /* default timeout */, m_isCategory,
|
||||||
// m_onStarted
|
// m_onStarted
|
||||||
{},
|
{},
|
||||||
@@ -125,29 +125,29 @@ using Observers = NSHashTable<Observer>;
|
|||||||
|
|
||||||
#pragma mark - Methods
|
#pragma mark - Methods
|
||||||
|
|
||||||
+ (void)saveQuery:(NSString *)query forInputLocale:(NSString *)inputLocale {
|
+ (void)saveQuery:(SearchQuery *)query {
|
||||||
if (!query || query.length == 0)
|
if (!query.text || query.text.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string locale = (!inputLocale || inputLocale.length == 0)
|
std::string locale = (!query.locale || query.locale == 0)
|
||||||
? [MWMSearch manager]->m_locale
|
? [MWMSearch manager]->m_locale
|
||||||
: inputLocale.UTF8String;
|
: query.locale.UTF8String;
|
||||||
std::string text = query.UTF8String;
|
std::string text = query.text.UTF8String;
|
||||||
GetFramework().GetSearchAPI().SaveSearchQuery({std::move(locale), std::move(text)});
|
GetFramework().GetSearchAPI().SaveSearchQuery({std::move(locale), std::move(text)});
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory {
|
+ (void)searchQuery:(SearchQuery *)query {
|
||||||
if (!query)
|
if (!query.text)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMSearch *manager = [MWMSearch manager];
|
MWMSearch *manager = [MWMSearch manager];
|
||||||
if (inputLocale.length != 0)
|
if (query.locale.length != 0)
|
||||||
manager->m_locale = inputLocale.UTF8String;
|
manager->m_locale = query.locale.UTF8String;
|
||||||
|
|
||||||
// Pass input query as-is without any normalization (precomposedStringWithCompatibilityMapping).
|
// Pass input query as-is without any normalization (precomposedStringWithCompatibilityMapping).
|
||||||
// Otherwise № -> No, and it's unexpectable for the search index.
|
// Otherwise № -> No, and it's unexpectable for the search index.
|
||||||
manager->m_query = query.UTF8String;
|
manager->m_query = query.text.UTF8String;
|
||||||
manager->m_isCategory = (isCategory == YES);
|
manager->m_isCategory = (query.source == SearchTextSourceCategory);
|
||||||
manager.textChanged = YES;
|
manager.textChanged = YES;
|
||||||
|
|
||||||
[manager update];
|
[manager update];
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
func test_GivenInitialState_WhenSelectCategory_ThenUpdateSearchResultsAndShowMap() {
|
func test_GivenInitialState_WhenSelectCategory_ThenUpdateSearchResultsAndShowMap() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("category")
|
let query = SearchQuery("category", source: .category)
|
||||||
interactor.handle(.didSelectText(searchText, isCategory: true))
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .halfScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .halfScreen)
|
||||||
XCTAssertEqual(view.viewModel.contentState, .searching)
|
XCTAssertEqual(view.viewModel.contentState, .searching)
|
||||||
XCTAssertEqual(view.viewModel.searchingText, searchText.text)
|
XCTAssertEqual(view.viewModel.searchingText, query.text)
|
||||||
XCTAssertEqual(view.viewModel.isTyping, false)
|
XCTAssertEqual(view.viewModel.isTyping, false)
|
||||||
|
|
||||||
let results = SearchResult.stubResults()
|
let results = SearchResult.stubResults()
|
||||||
@@ -62,8 +62,8 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
func test_GivenInitialState_WhenTypeText_ThenUpdateSearchResults() {
|
func test_GivenInitialState_WhenTypeText_ThenUpdateSearchResults() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didType(query))
|
||||||
|
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||||
XCTAssertEqual(view.viewModel.contentState, .searching)
|
XCTAssertEqual(view.viewModel.contentState, .searching)
|
||||||
@@ -83,8 +83,8 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
func test_GivenInitialState_WhenTapSearch_ThenUpdateSearchResultsAndShowMap() {
|
func test_GivenInitialState_WhenTapSearch_ThenUpdateSearchResultsAndShowMap() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didType(query))
|
||||||
|
|
||||||
let results = SearchResult.stubResults()
|
let results = SearchResult.stubResults()
|
||||||
searchManager.results = results
|
searchManager.results = results
|
||||||
@@ -94,7 +94,7 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
XCTAssertEqual(view.viewModel.searchingText, nil)
|
XCTAssertEqual(view.viewModel.searchingText, nil)
|
||||||
XCTAssertEqual(view.viewModel.isTyping, true)
|
XCTAssertEqual(view.viewModel.isTyping, true)
|
||||||
|
|
||||||
interactor.handle(.searchButtonDidTap(searchText))
|
interactor.handle(.searchButtonDidTap(query))
|
||||||
|
|
||||||
XCTAssertEqual(currentState, .searching)
|
XCTAssertEqual(currentState, .searching)
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .halfScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .halfScreen)
|
||||||
@@ -123,13 +123,13 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
XCTAssertEqual(view.viewModel.isTyping, true)
|
XCTAssertEqual(view.viewModel.isTyping, true)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
let results = SearchResult.stubResults()
|
let results = SearchResult.stubResults()
|
||||||
searchManager.results = results
|
searchManager.results = results
|
||||||
|
|
||||||
interactor.handle(.didSelectResult(results[0], withSearchText: searchText))
|
interactor.handle(.didSelectResult(results[0], withQuery: query))
|
||||||
if isIPad {
|
if isIPad {
|
||||||
XCTAssertEqual(currentState, .searching)
|
XCTAssertEqual(currentState, .searching)
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||||
@@ -156,13 +156,13 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
XCTAssertEqual(view.viewModel.isTyping, true)
|
XCTAssertEqual(view.viewModel.isTyping, true)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
let results = SearchResult.stubResults()
|
let results = SearchResult.stubResults()
|
||||||
searchManager.results = results
|
searchManager.results = results
|
||||||
|
|
||||||
interactor.handle(.didSelectResult(results[0], withSearchText: searchText))
|
interactor.handle(.didSelectResult(results[0], withQuery: query))
|
||||||
if isIPad {
|
if isIPad {
|
||||||
XCTAssertEqual(currentState, .searching)
|
XCTAssertEqual(currentState, .searching)
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||||
@@ -187,8 +187,8 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
func test_GivenSearchHasText_WhenClearSearch_ThenShowHistoryAndCategory() {
|
func test_GivenSearchHasText_WhenClearSearch_ThenShowHistoryAndCategory() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
interactor.handle(.clearButtonDidTap)
|
interactor.handle(.clearButtonDidTap)
|
||||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||||
@@ -200,8 +200,8 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
func test_GivenSearchExecuted_WhenNoResults_ThenShowNoResults() {
|
func test_GivenSearchExecuted_WhenNoResults_ThenShowNoResults() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("text")
|
let query = SearchQuery("text", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
searchManager.results = SearchOnMap.SearchResults([])
|
searchManager.results = SearchOnMap.SearchResults([])
|
||||||
interactor.onSearchCompleted()
|
interactor.onSearchCompleted()
|
||||||
@@ -209,17 +209,33 @@ final class SearchOnMapTests: XCTestCase {
|
|||||||
XCTAssertEqual(view.viewModel.contentState, .noResults)
|
XCTAssertEqual(view.viewModel.contentState, .noResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_GivenSearchIsActive_WhenSelectSuggestion_ThenSearchAgain() {
|
func test_GivenSearchIsActive_WhenSelectSuggestion_ThenReplaceWithSuggestion() {
|
||||||
interactor.handle(.openSearch)
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
let searchText = SearchOnMap.SearchText("old search")
|
let query = SearchQuery("ca", source: .typedText)
|
||||||
interactor.handle(.didType(searchText))
|
interactor.handle(.didType(query))
|
||||||
|
|
||||||
let suggestion = SearchResult(titleText: "", type: .suggestion, suggestion: "suggestion")
|
let result = SearchResult(titleText: "", type: .suggestion, suggestion: "cafe")
|
||||||
interactor.handle(.didSelectResult(suggestion, withSearchText: searchText))
|
interactor.handle(.didSelectResult(result, withQuery: query))
|
||||||
|
|
||||||
XCTAssertEqual(view.viewModel.searchingText, "suggestion")
|
XCTAssertEqual(view.viewModel.searchingText, "cafe")
|
||||||
XCTAssertEqual(view.viewModel.contentState, .searching)
|
XCTAssertEqual(view.viewModel.contentState, .searching)
|
||||||
|
XCTAssertEqual(view.viewModel.isTyping, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_GivenSearchIsActive_WhenPasteDeeplink_ThenShowResult() {
|
||||||
|
interactor.handle(.openSearch)
|
||||||
|
|
||||||
|
let query = SearchQuery("om://search?cll=42.0,44.0&query=Toilet", source: .deeplink)
|
||||||
|
interactor.handle(.didSelect(query))
|
||||||
|
|
||||||
|
let result = SearchResult(titleText: "some result", type: .regular, suggestion: "")
|
||||||
|
let results = SearchOnMap.SearchResults([result])
|
||||||
|
searchManager.results = results
|
||||||
|
interactor.onSearchCompleted()
|
||||||
|
|
||||||
|
XCTAssertEqual(view.viewModel.contentState, .results(results))
|
||||||
|
XCTAssertEqual(view.viewModel.isTyping, false) // No typing when deeplink is used
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,8 +271,8 @@ private class SearchManagerMock: SearchManager {
|
|||||||
self.observers.removeListener(observer)
|
self.observers.removeListener(observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func saveQuery(_ query: String, forInputLocale inputLocale: String) {}
|
static func save(_ query: SearchQuery) {}
|
||||||
static func searchQuery(_ query: String, forInputLocale inputLocale: String, withCategory isCategory: Bool) {}
|
static func searchQuery(_ query: SearchQuery) {}
|
||||||
static func showResult(at index: UInt) {}
|
static func showResult(at index: UInt) {}
|
||||||
static func showEverywhereSearchResultsOnMap() {}
|
static func showEverywhereSearchResultsOnMap() {}
|
||||||
static func showViewportSearchResultsOnMap() {}
|
static func showViewportSearchResultsOnMap() {}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ final class SearchOnMapHeaderView: UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchText: SearchOnMap.SearchText {
|
var searchQuery: SearchQuery {
|
||||||
SearchOnMap.SearchText(searchBar.text ?? "", locale: searchBar.textInputMode?.primaryLanguage)
|
SearchQuery(searchBar.text ?? "", locale: searchBar.textInputMode?.primaryLanguage, source: .typedText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ final class SearchOnMapInteractor: NSObject {
|
|||||||
return processTypedText(searchText)
|
return processTypedText(searchText)
|
||||||
case .clearButtonDidTap:
|
case .clearButtonDidTap:
|
||||||
return processClearButtonDidTap()
|
return processClearButtonDidTap()
|
||||||
case .didSelectText(let searchText, let isCategory):
|
case .didSelect(let searchText):
|
||||||
return processSelectedText(searchText, isCategory: isCategory)
|
return processSelectedText(searchText)
|
||||||
case .searchButtonDidTap(let searchText):
|
case .searchButtonDidTap(let searchText):
|
||||||
return processSearchButtonDidTap(searchText)
|
return processSearchButtonDidTap(searchText)
|
||||||
case .didSelectResult(let result, let searchText):
|
case .didSelectResult(let result, let query):
|
||||||
return processSelectedResult(result, searchText: searchText)
|
return processSelectedResult(result, query: query)
|
||||||
case .didSelectPlaceOnMap:
|
case .didSelectPlaceOnMap:
|
||||||
return isIPad ? .none : .setSearchScreenHidden(true)
|
return isIPad ? .none : .setSearchScreenHidden(true)
|
||||||
case .didDeselectPlaceOnMap:
|
case .didDeselectPlaceOnMap:
|
||||||
@@ -66,38 +66,31 @@ final class SearchOnMapInteractor: NSObject {
|
|||||||
return .clearSearch
|
return .clearSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processSearchButtonDidTap(_ searchText: SearchOnMap.SearchText) -> SearchOnMap.Response {
|
private func processSearchButtonDidTap(_ query: SearchQuery) -> SearchOnMap.Response {
|
||||||
searchManager.saveQuery(searchText.text,
|
searchManager.save(query)
|
||||||
forInputLocale: searchText.locale)
|
|
||||||
showResultsOnMap = true
|
showResultsOnMap = true
|
||||||
searchManager.showEverywhereSearchResultsOnMap()
|
searchManager.showEverywhereSearchResultsOnMap()
|
||||||
return .showOnTheMap
|
return .showOnTheMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processTypedText(_ searchText: SearchOnMap.SearchText) -> SearchOnMap.Response {
|
private func processTypedText(_ query: SearchQuery) -> SearchOnMap.Response {
|
||||||
isUpdatesDisabled = false
|
isUpdatesDisabled = false
|
||||||
searchManager.searchQuery(searchText.text,
|
searchManager.searchQuery(query)
|
||||||
forInputLocale: searchText.locale,
|
|
||||||
withCategory: false)
|
|
||||||
return .startSearching
|
return .startSearching
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processSelectedText(_ searchText: SearchOnMap.SearchText, isCategory: Bool) -> SearchOnMap.Response {
|
private func processSelectedText(_ query: SearchQuery) -> SearchOnMap.Response {
|
||||||
isUpdatesDisabled = false
|
isUpdatesDisabled = false
|
||||||
searchManager.saveQuery(searchText.text,
|
searchManager.save(query)
|
||||||
forInputLocale: searchText.locale)
|
searchManager.searchQuery(query)
|
||||||
searchManager.searchQuery(searchText.text,
|
|
||||||
forInputLocale: searchText.locale,
|
|
||||||
withCategory: isCategory)
|
|
||||||
showResultsOnMap = true
|
showResultsOnMap = true
|
||||||
return .selectText(searchText.text)
|
return .selectQuery(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processSelectedResult(_ result: SearchResult, searchText: SearchOnMap.SearchText) -> SearchOnMap.Response {
|
private func processSelectedResult(_ result: SearchResult, query: SearchQuery) -> SearchOnMap.Response {
|
||||||
switch result.itemType {
|
switch result.itemType {
|
||||||
case .regular:
|
case .regular:
|
||||||
searchManager.saveQuery(searchText.text,
|
searchManager.save(query)
|
||||||
forInputLocale:searchText.locale)
|
|
||||||
switch routingTooltipSearch {
|
switch routingTooltipSearch {
|
||||||
case .none:
|
case .none:
|
||||||
searchManager.showResult(at: result.index)
|
searchManager.showResult(at: result.index)
|
||||||
@@ -120,10 +113,11 @@ final class SearchOnMapInteractor: NSObject {
|
|||||||
}
|
}
|
||||||
return isIPad ? .none : .setSearchScreenHidden(true)
|
return isIPad ? .none : .setSearchScreenHidden(true)
|
||||||
case .suggestion:
|
case .suggestion:
|
||||||
searchManager.searchQuery(result.suggestion,
|
var suggestionQuery = SearchQuery(result.suggestion,
|
||||||
forInputLocale: searchText.locale,
|
locale: query.locale,
|
||||||
withCategory: result.isPureSuggest)
|
source: result.isPureSuggest ? .suggestion : .typedText)
|
||||||
return .selectText(result.suggestion)
|
searchManager.searchQuery(suggestionQuery)
|
||||||
|
return .selectQuery(suggestionQuery)
|
||||||
@unknown default:
|
@unknown default:
|
||||||
fatalError("Unsupported result type")
|
fatalError("Unsupported result type")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,8 @@ final class SearchOnMapManager: NSObject {
|
|||||||
interactor?.handle(.didStartDraggingMap)
|
interactor?.handle(.didStartDraggingMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchText(_ text: String, locale: String, isCategory: Bool) {
|
func searchText(_ searchText: SearchQuery) {
|
||||||
let searchText = SearchOnMap.SearchText(text, locale: locale)
|
interactor?.handle(.didSelect(searchText))
|
||||||
interactor?.handle(.didSelectText(searchText, isCategory: isCategory))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addObserver(_ observer: SearchOnMapManagerObserver) {
|
func addObserver(_ observer: SearchOnMapManagerObserver) {
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
@objcMembers
|
||||||
|
final class SearchQuery: NSObject {
|
||||||
|
let text: String
|
||||||
|
let locale: String
|
||||||
|
let source: SearchTextSource
|
||||||
|
|
||||||
|
init(_ text: String, locale: String? = nil, source: SearchTextSource) {
|
||||||
|
self.text = text
|
||||||
|
self.locale = locale ?? AppInfo.shared().languageId
|
||||||
|
self.source = source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum SearchOnMap {
|
enum SearchOnMap {
|
||||||
struct ViewModel: Equatable {
|
struct ViewModel: Equatable {
|
||||||
enum Content: Equatable {
|
enum Content: Equatable {
|
||||||
@@ -30,16 +43,6 @@ enum SearchOnMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SearchText {
|
|
||||||
let text: String
|
|
||||||
let locale: String
|
|
||||||
|
|
||||||
init(_ text: String, locale: String? = nil) {
|
|
||||||
self.text = text
|
|
||||||
self.locale = locale ?? AppInfo.shared().languageId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Request {
|
enum Request {
|
||||||
case openSearch
|
case openSearch
|
||||||
case hideSearch
|
case hideSearch
|
||||||
@@ -47,10 +50,10 @@ enum SearchOnMap {
|
|||||||
case didStartDraggingSearch
|
case didStartDraggingSearch
|
||||||
case didStartDraggingMap
|
case didStartDraggingMap
|
||||||
case didStartTyping
|
case didStartTyping
|
||||||
case didType(SearchText)
|
case didType(SearchQuery)
|
||||||
case didSelectText(SearchText, isCategory: Bool)
|
case didSelect(SearchQuery)
|
||||||
case didSelectResult(SearchResult, withSearchText: SearchText)
|
case didSelectResult(SearchResult, withQuery: SearchQuery)
|
||||||
case searchButtonDidTap(SearchText)
|
case searchButtonDidTap(SearchQuery)
|
||||||
case clearButtonDidTap
|
case clearButtonDidTap
|
||||||
case didSelectPlaceOnMap
|
case didSelectPlaceOnMap
|
||||||
case didDeselectPlaceOnMap
|
case didDeselectPlaceOnMap
|
||||||
@@ -63,7 +66,7 @@ enum SearchOnMap {
|
|||||||
case setIsTyping(Bool)
|
case setIsTyping(Bool)
|
||||||
case showHistoryAndCategory
|
case showHistoryAndCategory
|
||||||
case showResults(SearchResults, isSearchCompleted: Bool = false)
|
case showResults(SearchResults, isSearchCompleted: Bool = false)
|
||||||
case selectText(String?)
|
case selectQuery(SearchQuery)
|
||||||
case clearSearch
|
case clearSearch
|
||||||
case setSearchScreenHidden(Bool)
|
case setSearchScreenHidden(Bool)
|
||||||
case setSearchScreenCompact
|
case setSearchScreenCompact
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ final class SearchOnMapPresenter {
|
|||||||
searchResults.skipSuggestions()
|
searchResults.skipSuggestions()
|
||||||
}
|
}
|
||||||
viewModel.contentState = searchResults.isEmpty && isSearchCompleted ? .noResults : .results(searchResults)
|
viewModel.contentState = searchResults.isEmpty && isSearchCompleted ? .noResults : .results(searchResults)
|
||||||
case .selectText(let text):
|
case .selectQuery(let query):
|
||||||
viewModel.isTyping = false
|
|
||||||
viewModel.skipSuggestions = false
|
viewModel.skipSuggestions = false
|
||||||
viewModel.searchingText = text
|
viewModel.searchingText = query.text
|
||||||
viewModel.contentState = .searching
|
viewModel.contentState = .searching
|
||||||
|
viewModel.isTyping = false
|
||||||
viewModel.presentationStep = isRouting ? .hidden : .halfScreen
|
viewModel.presentationStep = isRouting ? .hidden : .halfScreen
|
||||||
case .clearSearch:
|
case .clearSearch:
|
||||||
viewModel.searchingText = ""
|
viewModel.searchingText = ""
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ protocol ModallyPresentedViewController: AnyObject {
|
|||||||
final class SearchOnMapViewController: UIViewController {
|
final class SearchOnMapViewController: UIViewController {
|
||||||
typealias ViewModel = SearchOnMap.ViewModel
|
typealias ViewModel = SearchOnMap.ViewModel
|
||||||
typealias Content = SearchOnMap.ViewModel.Content
|
typealias Content = SearchOnMap.ViewModel.Content
|
||||||
typealias SearchText = SearchOnMap.SearchText
|
|
||||||
|
|
||||||
fileprivate enum Constants {
|
fileprivate enum Constants {
|
||||||
static let estimatedRowHeight: CGFloat = 80
|
static let estimatedRowHeight: CGFloat = 80
|
||||||
@@ -417,7 +416,7 @@ extension SearchOnMapViewController: UITableViewDataSource {
|
|||||||
extension SearchOnMapViewController: UITableViewDelegate {
|
extension SearchOnMapViewController: UITableViewDelegate {
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
let result = searchResults[indexPath.row]
|
let result = searchResults[indexPath.row]
|
||||||
interactor?.handle(.didSelectResult(result, withSearchText: headerView.searchText))
|
interactor?.handle(.didSelectResult(result, withQuery: headerView.searchQuery))
|
||||||
tableView.deselectRow(at: indexPath, animated: true)
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,12 +436,12 @@ extension SearchOnMapViewController: SearchOnMapHeaderViewDelegate {
|
|||||||
interactor?.handle(.clearButtonDidTap)
|
interactor?.handle(.clearButtonDidTap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
interactor?.handle(.didType(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
interactor?.handle(.didType(SearchQuery(searchText, locale: searchBar.textInputMode?.primaryLanguage, source: .typedText)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||||
guard let searchText = searchBar.text, !searchText.isEmpty else { return }
|
guard let searchText = searchBar.text, !searchText.isEmpty else { return }
|
||||||
interactor?.handle(.searchButtonDidTap(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
interactor?.handle(.searchButtonDidTap(SearchQuery(searchText, locale: searchBar.textInputMode?.primaryLanguage, source: .typedText)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelButtonDidTap() {
|
func cancelButtonDidTap() {
|
||||||
@@ -456,8 +455,8 @@ extension SearchOnMapViewController: SearchOnMapHeaderViewDelegate {
|
|||||||
|
|
||||||
// MARK: - SearchTabViewControllerDelegate
|
// MARK: - SearchTabViewControllerDelegate
|
||||||
extension SearchOnMapViewController: SearchTabViewControllerDelegate {
|
extension SearchOnMapViewController: SearchTabViewControllerDelegate {
|
||||||
func searchTabController(_ viewController: SearchTabViewController, didSearch text: String, withCategory: Bool) {
|
func searchTabController(_ viewController: SearchTabViewController, didSearch query: SearchQuery) {
|
||||||
interactor?.handle(.didSelectText(SearchText(text, locale: nil), isCategory: withCategory))
|
interactor?.handle(.didSelect(query))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@objc(MWMSearchTabViewControllerDelegate)
|
@objc(MWMSearchTabViewControllerDelegate)
|
||||||
protocol SearchTabViewControllerDelegate: SearchOnMapScrollViewDelegate {
|
protocol SearchTabViewControllerDelegate: SearchOnMapScrollViewDelegate {
|
||||||
func searchTabController(_ viewController: SearchTabViewController, didSearch: String, withCategory: Bool)
|
func searchTabController(_ viewController: SearchTabViewController, didSearch: SearchQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(MWMSearchTabViewController)
|
@objc(MWMSearchTabViewController)
|
||||||
@@ -9,7 +9,7 @@ final class SearchTabViewController: TabViewController {
|
|||||||
case history = 0
|
case history = 0
|
||||||
case categories
|
case categories
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let selectedIndexKey = "SearchTabViewController_selectedIndexKey"
|
private static let selectedIndexKey = "SearchTabViewController_selectedIndexKey"
|
||||||
@objc weak var delegate: SearchTabViewControllerDelegate?
|
@objc weak var delegate: SearchTabViewControllerDelegate?
|
||||||
|
|
||||||
@@ -72,14 +72,15 @@ extension SearchTabViewController: SearchOnMapScrollViewDelegate {
|
|||||||
extension SearchTabViewController: SearchCategoriesViewControllerDelegate {
|
extension SearchTabViewController: SearchCategoriesViewControllerDelegate {
|
||||||
func categoriesViewController(_ viewController: SearchCategoriesViewController,
|
func categoriesViewController(_ viewController: SearchCategoriesViewController,
|
||||||
didSelect category: String) {
|
didSelect category: String) {
|
||||||
let query = L(category) + " "
|
let query = SearchQuery(L(category) + " ", source: .category)
|
||||||
delegate?.searchTabController(self, didSearch: query, withCategory: true)
|
delegate?.searchTabController(self, didSearch: query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SearchTabViewController: SearchHistoryViewControllerDelegate {
|
extension SearchTabViewController: SearchHistoryViewControllerDelegate {
|
||||||
func searchHistoryViewController(_ viewController: SearchHistoryViewController,
|
func searchHistoryViewController(_ viewController: SearchHistoryViewController,
|
||||||
didSelect query: String) {
|
didSelect query: String) {
|
||||||
delegate?.searchTabController(self, didSearch: query, withCategory: false)
|
let query = SearchQuery(query.trimmingCharacters(in: .whitespacesAndNewlines) + " ", source: .suggestion)
|
||||||
|
delegate?.searchTabController(self, didSearch: query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user