PickGroupMembersViewController.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "ThreemaFramework.h"
  21. #import "PickGroupMembersViewController.h"
  22. #import "ContactTableDataSource.h"
  23. #import "WorkContactTableDataSource.h"
  24. #import "ContactCell.h"
  25. #import "BundleUtil.h"
  26. #import "UserSettings.h"
  27. #import "LicenseStore.h"
  28. typedef enum : NSUInteger {
  29. ModeContact,
  30. ModeWorkContact
  31. } SelectionMode;
  32. @interface PickGroupMembersViewController ()
  33. @property SelectionMode mode;
  34. @property id<ContactGroupDataSource> currentDataSource;
  35. @property NSMutableSet *selectedMembers;
  36. @end
  37. @implementation PickGroupMembersViewController
  38. - (void)viewDidLoad {
  39. [super viewDidLoad];
  40. _mode = ModeContact;
  41. self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil];
  42. self.searchController.delegate = self;
  43. self.searchController.searchBar.showsScopeBar = NO;
  44. self.searchController.searchBar.scopeButtonTitles = nil;
  45. self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
  46. self.searchController.searchBar.delegate = self;
  47. self.searchController.searchResultsUpdater = self;
  48. self.searchController.searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  49. [self.searchController.searchBar sizeToFit];
  50. self.searchController.searchBar.barStyle = UISearchBarStyleMinimal;
  51. self.searchController.dimsBackgroundDuringPresentation = NO;
  52. self.definesPresentationContext = YES;
  53. if (@available(iOS 13.0, *)) {
  54. // iOS 13 and 13.1 have a bug. When searchbar is active, the navigationitem is not available
  55. // Bug should be fixed in 13.2
  56. self.searchController.hidesNavigationBarDuringPresentation = true;
  57. self.navigationController.view.backgroundColor = [Colors backgroundBaseColor];
  58. [self setModalInPresentation:true];
  59. } else {
  60. self.searchController.hidesNavigationBarDuringPresentation = false;
  61. }
  62. if (@available(iOS 11.0, *)) {
  63. self.navigationItem.searchController = _searchController;
  64. self.navigationItem.hidesSearchBarWhenScrolling = false;
  65. } else {
  66. self.definesPresentationContext = NO;
  67. [self.view addSubview:self.searchController.searchBar];
  68. }
  69. [self setupColors];
  70. }
  71. - (void)setupColors {
  72. [Colors updateTableView:self.tableView];
  73. if (@available(iOS 11.0, *)) {
  74. self.searchController.searchBar.barTintColor = [UIColor clearColor];
  75. self.searchController.searchBar.backgroundColor = [UIColor clearColor];
  76. UINavigationBar *navigationBar = self.navigationController.navigationBar;
  77. if (navigationBar) {
  78. navigationBar.barTintColor = [Colors backgroundBaseColor];
  79. }
  80. } else {
  81. self.searchController.searchBar.backgroundColor = [Colors backgroundBaseColor];
  82. UINavigationBar *navigationBar = self.navigationController.navigationBar;
  83. if (navigationBar) {
  84. navigationBar.barTintColor = [Colors backgroundBaseColor];
  85. }
  86. }
  87. [Colors updateSearchBar:_searchController.searchBar];
  88. if (@available(iOS 11.0, *)) {
  89. self.searchController.searchBar.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0.0, 7.0);
  90. self.navigationItem.largeTitleDisplayMode = [UserSettings sharedUserSettings].largeTitleDisplayMode;
  91. } else {
  92. }
  93. }
  94. -(void)viewWillAppear:(BOOL)animated {
  95. if ([LicenseStore requiresLicenseKey]) {
  96. self.navigationItem.titleView = self.segmentControl;
  97. } else {
  98. self.title = NSLocalizedString(@"members", nil);
  99. }
  100. if ([LicenseStore requiresLicenseKey] && [[WorkContactTableDataSource workContactTableDataSource] numberOfSectionsInTableView:self.tableView] > 0) {
  101. _mode = ModeWorkContact;
  102. }
  103. self.segmentControl.selectedSegmentIndex = _mode;
  104. [self segmentedControlChanged:nil];
  105. _searchController.searchBar.placeholder = NSLocalizedString(@"search", nil);
  106. if (_selectedMembers == nil) {
  107. _selectedMembers = [NSMutableSet set];
  108. }
  109. if (@available(iOS 11.0, *)) {
  110. self.searchController.searchBar.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0.0, 0.0);
  111. } else {
  112. self.tableView.contentInset = UIEdgeInsetsMake(_searchController.searchBar.frame.size.height, 0, 0, 0);
  113. }
  114. [super viewWillAppear:animated];
  115. }
  116. - (void)viewWillDisappear:(BOOL)animated {
  117. [super viewWillDisappear:animated];
  118. [_delegate group:_group updatedMembers:_selectedMembers];
  119. }
  120. - (void)setGroup:(GroupProxy *)group {
  121. _group = group;
  122. _selectedMembers = [NSMutableSet setWithSet: _group.members];
  123. }
  124. - (void)setMembers:(NSSet *)set {
  125. _selectedMembers = [NSMutableSet setWithSet: set];
  126. }
  127. - (NSSet *)getMembers {
  128. return [NSSet setWithSet:_selectedMembers];
  129. }
  130. #pragma mark - Table view data source
  131. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  132. return [_currentDataSource numberOfSectionsInTableView:tableView];
  133. }
  134. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  135. return [_currentDataSource tableView:tableView numberOfRowsInSection:section];
  136. }
  137. - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
  138. return [_currentDataSource sectionIndexTitlesForTableView:tableView];
  139. }
  140. - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
  141. return [_currentDataSource tableView:tableView sectionForSectionIndexTitle:title atIndex:index];
  142. }
  143. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
  144. return [_currentDataSource tableView:tableView titleForHeaderInSection:section];
  145. }
  146. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  147. {
  148. ContactCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"GroupContactCell"];
  149. Contact *contact;
  150. if (_mode == ModeWorkContact) {
  151. contact = [((WorkContactTableDataSource *) _currentDataSource) workContactAtIndexPath:indexPath];
  152. } else {
  153. contact = [((ContactTableDataSource *) _currentDataSource) contactAtIndexPath:indexPath];
  154. }
  155. cell.contact = contact;
  156. if ([_selectedMembers containsObject:cell.contact]) {
  157. cell.checkmarkView.image = [StyleKit check];
  158. cell.accessibilityTraits = UIAccessibilityTraitSelected;
  159. } else {
  160. cell.checkmarkView.image = [StyleKit uncheck];
  161. cell.accessibilityTraits = 0;
  162. }
  163. return cell;
  164. }
  165. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  166. [Colors updateTableViewCell:cell];
  167. }
  168. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  169. Contact *contact;
  170. if (_mode == ModeWorkContact) {
  171. contact = [((WorkContactTableDataSource *) _currentDataSource) workContactAtIndexPath:indexPath];
  172. } else {
  173. contact = [((ContactTableDataSource *) _currentDataSource) contactAtIndexPath:indexPath];
  174. }
  175. if ([_selectedMembers containsObject:contact]) {
  176. [_selectedMembers removeObject:contact];
  177. } else {
  178. int maxGroupMembers = [[BundleUtil objectForInfoDictionaryKey:@"ThreemaMaxGroupMembers"] intValue];
  179. if (_selectedMembers.count < maxGroupMembers) {
  180. [_selectedMembers addObject:contact];
  181. } else {
  182. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"too_many_members_title", nil) message:[NSString stringWithFormat:NSLocalizedString(@"too_many_members_message", nil), maxGroupMembers] actionOk:nil];
  183. }
  184. }
  185. [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  186. }
  187. #pragma mark - Search controller delegate
  188. - (void)willPresentSearchController:(UISearchController *)searchController {
  189. }
  190. - (void)willDismissSearchController:(UISearchController *)searchController {
  191. }
  192. -(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
  193. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  194. [self.tableView reloadData];
  195. }
  196. - (NSArray *)searchWordsForText:(NSString *)text {
  197. NSArray *searchWords = nil;
  198. if (text && [text length] > 0) {
  199. searchWords = [text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  200. }
  201. return searchWords;
  202. }
  203. #pragma mark - Actions
  204. - (IBAction)saveAction:(id)sender {
  205. NSSet *newMembers = _selectedMembers;
  206. NSSet *existingMembers = [NSSet setWithSet:_group.members];
  207. [self dismissViewControllerAnimated:YES completion:^{
  208. for (Contact *member in existingMembers) {
  209. if ([newMembers containsObject:member] == NO) {
  210. [_group adminRemoveMember:member];
  211. }
  212. }
  213. for (Contact *member in newMembers) {
  214. if ([_group.members containsObject:member] == NO) {
  215. [_group adminAddMember:member];
  216. }
  217. }
  218. }];
  219. }
  220. - (IBAction)cancelAction:(id)sender {
  221. [self dismissViewControllerAnimated:YES completion:nil];
  222. }
  223. - (IBAction)segmentedControlChanged:(id)sender {
  224. _mode = self.segmentControl.selectedSegmentIndex;
  225. switch (_mode) {
  226. case ModeContact:
  227. _currentDataSource = [ContactTableDataSource contactTableDataSourceWithMembers:_selectedMembers];
  228. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  229. break;
  230. case ModeWorkContact:
  231. _currentDataSource = [WorkContactTableDataSource workContactTableDataSourceWithMembers:_selectedMembers];
  232. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  233. break;
  234. default:
  235. break;
  236. }
  237. [self.tableView reloadData];
  238. }
  239. @end