// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2015-2020 Threema GmbH // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License, version 3, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #import "ThreemaFramework.h" #import "PickGroupMembersViewController.h" #import "ContactTableDataSource.h" #import "WorkContactTableDataSource.h" #import "ContactCell.h" #import "BundleUtil.h" #import "UserSettings.h" #import "LicenseStore.h" typedef enum : NSUInteger { ModeContact, ModeWorkContact } SelectionMode; @interface PickGroupMembersViewController () @property SelectionMode mode; @property id currentDataSource; @property NSMutableSet *selectedMembers; @end @implementation PickGroupMembersViewController - (void)viewDidLoad { [super viewDidLoad]; _mode = ModeContact; self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil]; self.searchController.delegate = self; self.searchController.searchBar.showsScopeBar = NO; self.searchController.searchBar.scopeButtonTitles = nil; self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag; self.searchController.searchBar.delegate = self; self.searchController.searchResultsUpdater = self; self.searchController.searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; [self.searchController.searchBar sizeToFit]; self.searchController.searchBar.barStyle = UISearchBarStyleMinimal; self.searchController.dimsBackgroundDuringPresentation = NO; self.definesPresentationContext = YES; if (@available(iOS 13.0, *)) { // iOS 13 and 13.1 have a bug. When searchbar is active, the navigationitem is not available // Bug should be fixed in 13.2 self.searchController.hidesNavigationBarDuringPresentation = true; self.navigationController.view.backgroundColor = [Colors backgroundBaseColor]; [self setModalInPresentation:true]; } else { self.searchController.hidesNavigationBarDuringPresentation = false; } if (@available(iOS 11.0, *)) { self.navigationItem.searchController = _searchController; self.navigationItem.hidesSearchBarWhenScrolling = false; } else { self.definesPresentationContext = NO; [self.view addSubview:self.searchController.searchBar]; } [self setupColors]; } - (void)setupColors { [Colors updateTableView:self.tableView]; if (@available(iOS 11.0, *)) { self.searchController.searchBar.barTintColor = [UIColor clearColor]; self.searchController.searchBar.backgroundColor = [UIColor clearColor]; UINavigationBar *navigationBar = self.navigationController.navigationBar; if (navigationBar) { navigationBar.barTintColor = [Colors backgroundBaseColor]; } } else { self.searchController.searchBar.backgroundColor = [Colors backgroundBaseColor]; UINavigationBar *navigationBar = self.navigationController.navigationBar; if (navigationBar) { navigationBar.barTintColor = [Colors backgroundBaseColor]; } } [Colors updateSearchBar:_searchController.searchBar]; if (@available(iOS 11.0, *)) { self.searchController.searchBar.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0.0, 7.0); self.navigationItem.largeTitleDisplayMode = [UserSettings sharedUserSettings].largeTitleDisplayMode; } else { } } -(void)viewWillAppear:(BOOL)animated { if ([LicenseStore requiresLicenseKey]) { self.navigationItem.titleView = self.segmentControl; } else { self.title = NSLocalizedString(@"members", nil); } if ([LicenseStore requiresLicenseKey] && [[WorkContactTableDataSource workContactTableDataSource] numberOfSectionsInTableView:self.tableView] > 0) { _mode = ModeWorkContact; } self.segmentControl.selectedSegmentIndex = _mode; [self segmentedControlChanged:nil]; _searchController.searchBar.placeholder = NSLocalizedString(@"search", nil); if (_selectedMembers == nil) { _selectedMembers = [NSMutableSet set]; } if (@available(iOS 11.0, *)) { self.searchController.searchBar.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0.0, 0.0); } else { self.tableView.contentInset = UIEdgeInsetsMake(_searchController.searchBar.frame.size.height, 0, 0, 0); } [super viewWillAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [_delegate group:_group updatedMembers:_selectedMembers]; } - (void)setGroup:(GroupProxy *)group { _group = group; _selectedMembers = [NSMutableSet setWithSet: _group.members]; } - (void)setMembers:(NSSet *)set { _selectedMembers = [NSMutableSet setWithSet: set]; } - (NSSet *)getMembers { return [NSSet setWithSet:_selectedMembers]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [_currentDataSource numberOfSectionsInTableView:tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [_currentDataSource tableView:tableView numberOfRowsInSection:section]; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return [_currentDataSource sectionIndexTitlesForTableView:tableView]; } - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { return [_currentDataSource tableView:tableView sectionForSectionIndexTitle:title atIndex:index]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [_currentDataSource tableView:tableView titleForHeaderInSection:section]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ContactCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"GroupContactCell"]; Contact *contact; if (_mode == ModeWorkContact) { contact = [((WorkContactTableDataSource *) _currentDataSource) workContactAtIndexPath:indexPath]; } else { contact = [((ContactTableDataSource *) _currentDataSource) contactAtIndexPath:indexPath]; } cell.contact = contact; if ([_selectedMembers containsObject:cell.contact]) { cell.checkmarkView.image = [StyleKit check]; cell.accessibilityTraits = UIAccessibilityTraitSelected; } else { cell.checkmarkView.image = [StyleKit uncheck]; cell.accessibilityTraits = 0; } return cell; } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { [Colors updateTableViewCell:cell]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { Contact *contact; if (_mode == ModeWorkContact) { contact = [((WorkContactTableDataSource *) _currentDataSource) workContactAtIndexPath:indexPath]; } else { contact = [((ContactTableDataSource *) _currentDataSource) contactAtIndexPath:indexPath]; } if ([_selectedMembers containsObject:contact]) { [_selectedMembers removeObject:contact]; } else { int maxGroupMembers = [[BundleUtil objectForInfoDictionaryKey:@"ThreemaMaxGroupMembers"] intValue]; if (_selectedMembers.count < maxGroupMembers) { [_selectedMembers addObject:contact]; } else { [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"too_many_members_title", nil) message:[NSString stringWithFormat:NSLocalizedString(@"too_many_members_message", nil), maxGroupMembers] actionOk:nil]; } } [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } #pragma mark - Search controller delegate - (void)willPresentSearchController:(UISearchController *)searchController { } - (void)willDismissSearchController:(UISearchController *)searchController { } -(void)updateSearchResultsForSearchController:(UISearchController *)searchController { [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]]; [self.tableView reloadData]; } - (NSArray *)searchWordsForText:(NSString *)text { NSArray *searchWords = nil; if (text && [text length] > 0) { searchWords = [text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; } return searchWords; } #pragma mark - Actions - (IBAction)saveAction:(id)sender { NSSet *newMembers = _selectedMembers; NSSet *existingMembers = [NSSet setWithSet:_group.members]; [self dismissViewControllerAnimated:YES completion:^{ for (Contact *member in existingMembers) { if ([newMembers containsObject:member] == NO) { [_group adminRemoveMember:member]; } } for (Contact *member in newMembers) { if ([_group.members containsObject:member] == NO) { [_group adminAddMember:member]; } } }]; } - (IBAction)cancelAction:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } - (IBAction)segmentedControlChanged:(id)sender { _mode = self.segmentControl.selectedSegmentIndex; switch (_mode) { case ModeContact: _currentDataSource = [ContactTableDataSource contactTableDataSourceWithMembers:_selectedMembers]; [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]]; break; case ModeWorkContact: _currentDataSource = [WorkContactTableDataSource workContactTableDataSourceWithMembers:_selectedMembers]; [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]]; break; default: break; } [self.tableView reloadData]; } @end