co-maps/iphone/Maps/Core/Search/MWMSearch.mm

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

298 lines
7.9 KiB
Text
Raw Permalink Normal View History

2025-11-22 13:58:55 +01:00
#import "MWMSearch.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "SearchResult+Core.h"
#import "SwiftBridge.h"
#include <CoreApi/MWMTypes.h>
#include <CoreApi/Framework.h>
#include "platform/network_policy.hpp"
namespace {
using Observer = id<MWMSearchObserver>;
using Observers = NSHashTable<Observer>;
} // namespace
@interface MWMSearch () <MWMFrameworkDrapeObserver>
@property(nonatomic) NSUInteger suggestionsCount;
@property(nonatomic) SearchMode searchMode;
@property(nonatomic) BOOL textChanged;
@property(nonatomic) Observers * observers;
@property(nonatomic) NSUInteger lastSearchTimestamp;
@property(nonatomic) SearchIndex * itemsIndex;
@property(nonatomic) NSInteger searchCount;
@end
@implementation MWMSearch {
std::string m_query;
std::string m_locale;
bool m_isCategory;
search::Results m_everywhereResults;
search::Results m_viewportResults;
}
#pragma mark - Instance
+ (MWMSearch *)manager {
static MWMSearch *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] initManager];
});
return manager;
}
- (instancetype)initManager {
self = [super init];
if (self) {
_observers = [Observers weakObjectsHashTable];
[MWMFrameworkListener addObserver:self];
}
return self;
}
- (void)searchEverywhere {
self.lastSearchTimestamp += 1;
NSUInteger const timestamp = self.lastSearchTimestamp;
search::EverywhereSearchParams params{
m_query, m_locale, {} /* default timeout */, m_isCategory,
// m_onResults
[self, timestamp](search::Results results, std::vector<search::ProductInfo> productInfo)
{
// Store the flag first, because we will make move next.
bool const isEndMarker = results.IsEndMarker();
if (timestamp == self.lastSearchTimestamp)
{
self.suggestionsCount = results.GetSuggestsCount();
self->m_everywhereResults = std::move(results);
[self onSearchResultsUpdated];
}
if (isEndMarker)
self.searchCount -= 1;
}
};
GetFramework().GetSearchAPI().SearchEverywhere(std::move(params));
self.searchCount += 1;
}
- (void)searchInViewport {
search::ViewportSearchParams params {
m_query, m_locale, {} /* default timeout */, m_isCategory,
// m_onStarted
{},
// m_onCompleted
[self](search::Results results)
{
if (!results.IsEndMarker())
return;
if (!results.IsEndedCancelled())
self->m_viewportResults = std::move(results);
}
};
GetFramework().GetSearchAPI().SearchInViewport(std::move(params));
}
- (void)update {
if (m_query.empty())
return;
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
+ (void)addObserver:(id<MWMSearchObserver>)observer {
[[MWMSearch manager].observers addObject:observer];
}
+ (void)removeObserver:(id<MWMSearchObserver>)observer {
[[MWMSearch manager].observers removeObject:observer];
}
#pragma mark - Methods
+ (void)saveQuery:(SearchQuery *)query {
if (!query.text || query.text.length == 0)
return;
std::string locale = (!query.locale || query.locale == 0)
? [MWMSearch manager]->m_locale
: query.locale.UTF8String;
std::string text = query.text.UTF8String;
GetFramework().GetSearchAPI().SaveSearchQuery({std::move(locale), std::move(text)});
}
+ (void)searchQuery:(SearchQuery *)query {
if (!query.text)
return;
MWMSearch *manager = [MWMSearch manager];
if (query.locale.length != 0)
manager->m_locale = query.locale.UTF8String;
// Pass input query as-is without any normalization (precomposedStringWithCompatibilityMapping).
// Otherwise № -> No, and it's unexpectable for the search index.
manager->m_query = query.text.UTF8String;
manager->m_isCategory = (query.source == SearchTextSourceCategory);
manager.textChanged = YES;
[manager reset];
[manager update];
}
+ (void)showResultAtIndex:(NSUInteger)index {
auto const & result = [MWMSearch manager]->m_everywhereResults[index];
GetFramework().StopLocationFollow();
GetFramework().SelectSearchResult(result, true);
}
+ (SearchResult *)resultWithContainerIndex:(NSUInteger)index {
SearchResult * result = [[SearchResult alloc] initWithResult:[MWMSearch manager]->m_everywhereResults[index]
itemType:[MWMSearch resultTypeWithRow:index]
index:index];
return result;
}
+ (NSArray<SearchResult *> *)getResults {
NSMutableArray<SearchResult *> * results = [[NSMutableArray alloc] initWithCapacity:MWMSearch.resultsCount];
for (NSUInteger i = 0; i < MWMSearch.resultsCount; ++i) {
SearchResult * result = [MWMSearch resultWithContainerIndex:i];
[results addObject:result];
}
return [results copy];
}
+ (SearchItemType)resultTypeWithRow:(NSUInteger)row {
auto itemsIndex = [MWMSearch manager].itemsIndex;
return [itemsIndex resultTypeWithRow:row];
}
+ (NSUInteger)containerIndexWithRow:(NSUInteger)row {
auto itemsIndex = [MWMSearch manager].itemsIndex;
return [itemsIndex resultContainerIndexWithRow:row];
}
- (void)reset {
self.lastSearchTimestamp += 1;
GetFramework().GetSearchAPI().CancelAllSearches();
m_everywhereResults.Clear();
m_viewportResults.Clear();
[self onSearchResultsUpdated];
}
+ (void)clear {
auto manager = [MWMSearch manager];
manager->m_query.clear();
manager.suggestionsCount = 0;
[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;
}
- (void)updateItemsIndexWithBannerReload:(BOOL)reloadBanner {
auto const resultsCount = self->m_everywhereResults.GetCount();
auto const itemsIndex = [[SearchIndex alloc] initWithSuggestionsCount:self.suggestionsCount
resultsCount:resultsCount];
[itemsIndex build];
self.itemsIndex = itemsIndex;
}
#pragma mark - Notifications
- (void)onSearchStarted {
for (Observer observer in self.observers) {
if ([observer respondsToSelector:@selector(onSearchStarted)])
[observer onSearchStarted];
}
}
- (void)onSearchCompleted {
[self updateItemsIndexWithBannerReload:YES];
for (Observer observer in self.observers) {
if ([observer respondsToSelector:@selector(onSearchCompleted)])
[observer onSearchCompleted];
}
}
- (void)onSearchResultsUpdated {
[self updateItemsIndexWithBannerReload:NO];
for (Observer observer in self.observers) {
if ([observer respondsToSelector:@selector(onSearchResultsUpdated)])
[observer onSearchResultsUpdated];
}
}
#pragma mark - MWMFrameworkDrapeObserver
- (void)processViewportChangedEvent {
if (!GetFramework().GetSearchAPI().IsViewportSearchActive())
return;
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
- (void)setSearchCount:(NSInteger)searchCount {
NSAssert((searchCount >= 0) && ((_searchCount == searchCount - 1) || (_searchCount == searchCount + 1)),
@"Invalid search count update");
if (searchCount > 0)
[self onSearchStarted];
else if (searchCount == 0)
[self onSearchCompleted];
_searchCount = searchCount;
}
@end