Files
bundesmessenger-ios/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m
T
JanNiklas Grabowski b298dedc22 chore: update from foss 1.11.19 (MESSENGER-6656)
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19

* commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1':
  finish version++
  version++
  comments
  update submodule
  remove obsolete tests
  removed unused code
  update submodule
  fix
  Libolm removal
  update license macro
  update license
  Prepare for new sprint

# Conflicts:
#	Config/AppVersion.xcconfig
#	IDETemplateMacros.plist
#	LICENSE
#	README.md
#	Riot/Categories/MXSession+Riot.m
#	Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
#	Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
#	Riot/Managers/KeyValueStorage/KeyValueStore.swift
#	Riot/Managers/KeyValueStorage/KeychainStore.swift
#	Riot/Managers/KeyValueStorage/MemoryStore.swift
#	Riot/Managers/PushNotification/PushNotificationService.m
#	Riot/Managers/Settings/RiotSettings.swift
#	Riot/Managers/Settings/Shared/RiotSharedSettings.swift
#	Riot/Modules/Analytics/AnalyticsUIElement.swift
#	Riot/Modules/Application/AppCoordinator.swift
#	Riot/Modules/Application/LegacyAppDelegate.h
#	Riot/Modules/Application/LegacyAppDelegate.m
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.h
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.m
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m
#	Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
#	Riot/Modules/Common/Recents/RecentsViewController.m
#	Riot/Modules/Common/WebViewController/WebViewViewController.m
#	Riot/Modules/Contacts/Details/ContactDetailsViewController.m
#	Riot/Modules/Contacts/Views/ContactTableViewCell.m
#	Riot/Modules/Favorites/FavouritesViewController.h
#	Riot/Modules/Favorites/FavouritesViewController.m
#	Riot/Modules/GlobalSearch/UnifiedSearchViewController.m
#	Riot/Modules/People/PeopleViewController.h
#	Riot/Modules/People/PeopleViewController.m
#	Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift
#	Riot/Modules/Room/DataSources/RoomDataSource.m
#	Riot/Modules/Room/Files/RoomFilesViewController.m
#	Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
#	Riot/Modules/Room/Members/RoomParticipantsViewController.m
#	Riot/Modules/Room/RoomViewController.m
#	Riot/Modules/Room/Settings/RoomSettingsViewController.m
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift
#	Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h
#	Riot/Modules/Rooms/RoomsViewController.h
#	Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift
#	Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift
#	Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift
#	Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift
#	Riot/Modules/SetPinCode/PinCodePreferences.swift
#	Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
#	Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m
#	Riot/Modules/Settings/Security/SecurityViewController.m
#	Riot/Modules/Settings/SettingsViewController.m
#	Riot/Modules/SplitView/SplitViewCoordinator.swift
#	Riot/Modules/SplitView/SplitViewCoordinatorType.swift
#	Riot/Modules/StartChat/StartChatViewController.m
#	Riot/Modules/TabBar/MasterTabBarController.h
#	Riot/Modules/TabBar/MasterTabBarController.m
#	Riot/Utils/EventFormatter.m
#	Riot/Utils/HTMLFormatter.swift
#	Riot/Utils/Tools.m
#	RiotNSE/NotificationService.swift
2024-10-18 15:45:54 +02:00

493 lines
16 KiB
Objective-C

/*
Copyright 2024 New Vector Ltd.
Copyright 2017 Vector Creations Ltd
Copyright (c) 2021 BWI GmbH
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
#import "UnifiedSearchViewController.h"
#import "UnifiedSearchRecentsDataSource.h"
#import "RecentsViewController.h"
#import "RoomDataSource.h"
#import "RoomViewController.h"
#import "DirectoryViewController.h"
#import "ContactDetailsViewController.h"
#import "SettingsViewController.h"
#import "HomeMessagesSearchViewController.h"
#import "HomeMessagesSearchDataSource.h"
#import "HomeFilesSearchViewController.h"
#import "FilesSearchCellData.h"
#import "GeneratedInterface-Swift.h"
#import "GBDeviceInfo_iOS.h"
@interface UnifiedSearchViewController ()
{
RecentsViewController *recentsViewController;
UnifiedSearchRecentsDataSource *recentsDataSource;
HomeMessagesSearchViewController *messagesSearchViewController;
HomeMessagesSearchDataSource *messagesSearchDataSource;
HomeFilesSearchViewController *filesSearchViewController;
MXKSearchDataSource *filesSearchDataSource;
ContactsTableViewController *peopleSearchViewController;
ContactsDataSource *peopleSearchDataSource;
// Current alert (if any).
UIAlertController *currentAlert;
}
@end
@implementation UnifiedSearchViewController
+ (instancetype)instantiate
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UnifiedSearchViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"UnifiedSearchViewController"];
return viewController;
}
- (void)finalizeInit
{
[super finalizeInit];
// The navigation bar tint color and the rageShake Manager are handled by super (see SegmentedViewController).
}
- (void)viewDidLoad
{
// Set up the SegmentedVC tabs before calling [super viewDidLoad]
NSMutableArray* viewControllers = [[NSMutableArray alloc] init];
NSMutableArray* titles = [[NSMutableArray alloc] init];
[titles addObject:[VectorL10n searchRooms]];
recentsViewController = [RecentsViewController recentListViewController];
recentsViewController.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSearchRooms];
recentsViewController.enableSearchBar = NO;
[viewControllers addObject:recentsViewController];
messagesSearchViewController = [HomeMessagesSearchViewController searchViewController];
if (BWIBuildSettings.shared.showUnifiedSearchViewMessagesTab)
{
messagesSearchViewController.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSearchMessages];
[viewControllers addObject:messagesSearchViewController];
}
// Add search People tab
[titles addObject:[VectorL10n searchPeople]];
peopleSearchViewController = [ContactsTableViewController contactsTableViewController];
peopleSearchViewController.contactsTableViewControllerDelegate = self;
peopleSearchViewController.disableFindYourContactsFooter = YES;
peopleSearchViewController.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSearchPeople];
[viewControllers addObject:peopleSearchViewController];
filesSearchViewController = [HomeFilesSearchViewController searchViewController];
if (BWIBuildSettings.shared.showUnifiedSearchViewFilesTab)
{
filesSearchViewController.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSearchFiles];
[viewControllers addObject:filesSearchViewController];
}
[self initWithTitles:titles viewControllers:viewControllers defaultSelected:0];
[super viewDidLoad];
// Initialize here the data sources if a matrix session has been already set.
[self initializeDataSources];
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.searchBar.placeholder = [VectorL10n searchDefaultPlaceholder];
[super showSearch:NO];
}
- (void)destroy
{
[super destroy];
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
self.searchBar.delegate = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Let's child display the loading not the home view controller
if (self.activityIndicator)
{
[self.activityIndicator stopAnimating];
self.activityIndicator = nil;
}
// Reset searches
[recentsDataSource searchWithPatterns:nil];
[self updateSearch];
if (BuildSettings.newAppLayoutEnabled)
{
[self.searchBar vc_searchTextField].backgroundColor = nil;
[self vc_setLargeTitleDisplayMode: UINavigationItemLargeTitleDisplayModeAutomatic];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
if (!self.searchBarHidden && self.extendedLayoutIncludesOpaqueBars)
{
// if a search bar is visible, navigationBar height will be increased. Below code will force update layout on previous view controller.
[self.navigationController.view setNeedsLayout]; // force update layout
[self.navigationController.view layoutIfNeeded]; // to fix height of the navigation bar
}
[super viewWillDisappear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.splitViewController && !self.splitViewController.isCollapsed)
{
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side onscreen,
// the selected room (if any) is highlighted.
[self refreshCurrentSelectedCellInChild:YES];
}
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return ThemeService.shared.theme.statusBarStyle;
}
#pragma mark -
- (MXEvent*)selectedSearchEvent
{
if (messagesSearchViewController.selectedEvent)
{
return messagesSearchViewController.selectedEvent;
}
return filesSearchViewController.selectedEvent;
}
- (MXSession*)selectedSearchEventSession
{
if (messagesSearchViewController.selectedEvent)
{
return messagesSearchDataSource.mxSession;
}
return filesSearchDataSource.mxSession;
}
#pragma mark -
- (void)initializeDataSources
{
MXSession *mainSession = self.mainSession;
if (mainSession)
{
// Init the recents data source
RecentsListService *recentsListService = [[RecentsListService alloc] initWithSession:mainSession];
recentsDataSource = [[UnifiedSearchRecentsDataSource alloc] initWithMatrixSession:mainSession
recentsListService:recentsListService];
[recentsViewController displayList:recentsDataSource];
// Init the search for messages
messagesSearchDataSource = [[HomeMessagesSearchDataSource alloc] initWithMatrixSession:mainSession];
[messagesSearchViewController displaySearch:messagesSearchDataSource];
// Init the search for messages
filesSearchDataSource = [[MXKSearchDataSource alloc] initWithMatrixSession:mainSession];
filesSearchDataSource.roomEventFilter.containsURL = YES;
filesSearchDataSource.shouldShowRoomDisplayName = YES;
[filesSearchDataSource registerCellDataClass:FilesSearchCellData.class forCellIdentifier:kMXKSearchCellDataIdentifier];
[filesSearchViewController displaySearch:filesSearchDataSource];
// Init the search for people
peopleSearchDataSource = [[ContactsDataSource alloc] initWithMatrixSession:mainSession];
peopleSearchDataSource.showLocalContacts = NO;
peopleSearchDataSource.areSectionsShrinkable = YES;
peopleSearchDataSource.displaySearchInputInContactsList = YES;
peopleSearchDataSource.contactCellAccessoryImage = [AssetImages.disclosureIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textSecondaryColor];;
[peopleSearchViewController displayList:peopleSearchDataSource];
// Check whether there are others sessions
NSArray* mxSessions = self.mxSessions;
if (mxSessions.count > 1)
{
for (MXSession *mxSession in mxSessions)
{
if (mxSession != mainSession)
{
// Add the session to the recents data source
[recentsDataSource addMatrixSession:mxSession];
// FIXME: Update messagesSearchDataSource and filesSearchDataSource
}
}
}
}
}
- (void)addMatrixSession:(MXSession *)mxSession
{
// Check whether the controller'€™s view is loaded into memory.
if (recentsViewController)
{
// Check whether the data sources have been initialized.
if (!recentsDataSource)
{
// Add first the session. The updated sessions list will be used during data sources initialization.
[super addMatrixSession:mxSession];
// Prepare data sources and return
[self initializeDataSources];
return;
}
else
{
// Add the session to the existing recents data source
[recentsDataSource addMatrixSession:mxSession];
// FIXME: Update messagesSearchDataSource and filesSearchDataSource
}
}
[super addMatrixSession:mxSession];
}
- (void)removeMatrixSession:(MXSession *)mxSession
{
[recentsDataSource removeMatrixSession:mxSession];
// Check whether there are others sessions
if (!recentsDataSource.mxSessions.count)
{
[recentsViewController displayList:nil];
[recentsDataSource destroy];
recentsDataSource = nil;
}
// FIXME: Handle correctly messagesSearchDataSource and filesSearchDataSource
[super removeMatrixSession:mxSession];
}
- (void)showPublicRoomsDirectory
{
// Force hiding the keyboard
[self.searchBar resignFirstResponder];
[self performSegueWithIdentifier:@"showDirectory" sender:self];
}
#pragma mark - Override MXKViewController
- (void)startActivityIndicator
{
// Redirect the operation to the currently displayed VC
// It is a MXKViewController or a MXKTableViewController. So it supports startActivityIndicator
[self.selectedViewController performSelector:@selector(startActivityIndicator)];
}
- (void)stopActivityIndicator
{
// The selected view controller mwy have changed since the call of [self startActivityIndicator]
// So, stop the activity indicator for all children
for (UIViewController *viewController in self.viewControllers)
{
[viewController performSelector:@selector(stopActivityIndicator)];
}
}
#pragma mark - Override SegmentedViewController
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
[super setSelectedIndex:selectedIndex];
if (self.selectedViewController == peopleSearchViewController)
{
self.searchBar.placeholder = [BWIL10n searchPeoplePlaceholder];
}
else
{
self.searchBar.placeholder = [VectorL10n searchDefaultPlaceholder];
}
[self updateSearch];
}
#pragma mark - Internal methods
// Made the currently displayed child update its selected cell
- (void)refreshCurrentSelectedCellInChild:(BOOL)forceVisible
{
// TODO: Manage other children than recents
[recentsViewController refreshCurrentSelectedCell:forceVisible];
[peopleSearchViewController refreshCurrentSelectedCell:forceVisible];
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:@"showDirectory"])
{
DirectoryViewController *directoryViewController = segue.destinationViewController;
[directoryViewController displayWitDataSource:recentsDataSource.publicRoomsDirectoryDataSource];
}
// Hide back button title
[self vc_removeBackTitle];
}
#pragma mark - Search
- (void)hideSearch:(BOOL)animated
{
[self withdrawViewControllerAnimated:animated completion:nil];
}
// Update search results under the currently selected tab
- (void)updateSearch
{
if (self.searchBar.text.length)
{
recentsDataSource.hideRecents = NO;
// Forward the search request to the data source
if (self.selectedViewController == recentsViewController)
{
// Do a AND search on words separated by a space
NSArray *patterns = [self.searchBar.text componentsSeparatedByString:@" "];
[recentsDataSource searchWithPatterns:patterns];
recentsViewController.shouldScrollToTopOnRefresh = YES;
}
else if (self.selectedViewController == messagesSearchViewController)
{
// Launch the search only if the keyboard is no more visible
if (!self.searchBar.isFirstResponder)
{
// Do it asynchronously to give time to messagesSearchViewController to be set up
// so that it can display its loading wheel
dispatch_async(dispatch_get_main_queue(), ^{
[self->messagesSearchDataSource searchMessages:self.searchBar.text force:NO];
self->messagesSearchViewController.shouldScrollToBottomOnRefresh = YES;
});
}
}
else if (self.selectedViewController == peopleSearchViewController)
{
[peopleSearchDataSource searchWithPattern:self.searchBar.text forceReset:NO];
}
else if (self.selectedViewController == filesSearchViewController)
{
// Launch the search only if the keyboard is no more visible
if (!self.searchBar.isFirstResponder)
{
// Do it asynchronously to give time to filesSearchViewController to be set up
// so that it can display its loading wheel
dispatch_async(dispatch_get_main_queue(), ^{
[self->filesSearchDataSource searchMessages:self.searchBar.text force:NO];
self->filesSearchViewController.shouldScrollToBottomOnRefresh = YES;
});
}
}
}
else
{
// Nothing to search, show only the public dictionary
recentsDataSource.hideRecents = YES;
// Reset search result (if any)
[recentsDataSource searchWithPatterns:nil];
if (messagesSearchDataSource.searchText.length)
{
[messagesSearchDataSource searchMessages:nil force:NO];
}
[peopleSearchDataSource searchWithPattern:nil forceReset:NO];
if (filesSearchDataSource.searchText.length)
{
[filesSearchDataSource searchMessages:nil force:NO];
}
}
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
AccountRestrictionService* service = [[AccountRestrictionService alloc] initWithMxSession:self.mainSession];
if (self.selectedViewController == recentsViewController)
{
if (![service isRoomAccessRestriction]) {
// As the public room search is local, it can be updated on each text change
[self updateSearch];
}
}
else if (self.selectedViewController == peopleSearchViewController)
{
if (![service isAddressListRestriction]) {
// As the contact search is local, it can be updated on each text change
[self updateSearch];
}
}
else if (!self.searchBar.text.length)
{
// Reset message search if any
[self updateSearch];
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
if (self.selectedViewController == messagesSearchViewController || self.selectedViewController == filesSearchViewController)
{
// As the messages/files search is done homeserver-side, launch it only on the "Search" button
[self updateSearch];
}
}
#pragma mark - ContactsTableViewControllerDelegate
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact
{
// Force hiding the keyboard
[self.searchBar resignFirstResponder];
[[AppDelegate theDelegate].masterTabBarController selectContact:contact];
}
@end