123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- /**
- * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- #import "AddParticipantsTableViewController.h"
- #import "NCAPIController.h"
- #import "NCAppBranding.h"
- #import "NCContact.h"
- #import "NCDatabaseManager.h"
- #import "NCUserInterfaceController.h"
- #import "PlaceholderView.h"
- #import "ResultMultiSelectionTableViewController.h"
- #import "NextcloudTalk-Swift.h"
- @interface AddParticipantsTableViewController () <UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating>
- {
- NSMutableDictionary *_participants;
- NSArray *_indexes;
- NCRoom *_room;
- NSArray *_participantsInRoom;
- UISearchController *_searchController;
- ResultMultiSelectionTableViewController *_resultTableViewController;
- NSMutableArray *_selectedParticipants;
- PlaceholderView *_participantsBackgroundView;
- NSTimer *_searchTimer;
- NSURLSessionTask *_searchParticipantsTask;
- UIActivityIndicatorView *_addingParticipantsIndicator;
- BOOL _errorAddingParticipants;
- }
- @end
- @implementation AddParticipantsTableViewController
- - (instancetype)initForRoom:(NCRoom *)room
- {
- self = [super init];
- if (!self) {
- return nil;
- }
-
- if (room) {
- _room = room;
- _participantsInRoom = [room.participants valueForKey:@"self"];
- }
- _participants = [[NSMutableDictionary alloc] init];
- _indexes = [[NSArray alloc] init];
- _selectedParticipants = [[NSMutableArray alloc] init];
- _addingParticipantsIndicator = [[UIActivityIndicatorView alloc] init];
- _addingParticipantsIndicator.color = [NCAppBranding themeTextColor];
- return self;
- }
- - (instancetype)initWithParticipants:(NSArray<NCUser *> *)participants
- {
- self = [self initForRoom:nil];
- if (!self) {
- return nil;
- }
- _selectedParticipants = [[NSMutableArray alloc] initWithArray:participants];
- return self;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
- [self.tableView registerNib:[UINib nibWithNibName:kContactsTableCellNibName bundle:nil] forCellReuseIdentifier:kContactCellIdentifier];
- self.tableView.separatorInset = UIEdgeInsetsMake(0, 72, 0, 0);
- self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];
-
- _resultTableViewController = [[ResultMultiSelectionTableViewController alloc] init];
- _resultTableViewController.selectedParticipants = _selectedParticipants;
- _resultTableViewController.room = _room;
- _searchController = [[UISearchController alloc] initWithSearchResultsController:_resultTableViewController];
- _searchController.searchResultsUpdater = self;
- [_searchController.searchBar sizeToFit];
- UIColor *themeColor = [NCAppBranding themeColor];
- UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
- [appearance configureWithOpaqueBackground];
- appearance.backgroundColor = themeColor;
- appearance.titleTextAttributes = @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]};
- self.navigationItem.standardAppearance = appearance;
- self.navigationItem.compactAppearance = appearance;
- self.navigationItem.scrollEdgeAppearance = appearance;
- self.navigationItem.searchController = _searchController;
- self.navigationItem.searchController.searchBar.searchTextField.backgroundColor = [NCUtils searchbarBGColorForColor:themeColor];
- if (@available(iOS 16.0, *)) {
- self.navigationItem.preferredSearchBarPlacement = UINavigationItemSearchBarPlacementStacked;
- }
-
- _searchController.searchBar.tintColor = [NCAppBranding themeTextColor];
- UITextField *searchTextField = [_searchController.searchBar valueForKey:@"searchField"];
- UIButton *clearButton = [searchTextField valueForKey:@"_clearButton"];
- searchTextField.tintColor = [NCAppBranding themeTextColor];
- searchTextField.textColor = [NCAppBranding themeTextColor];
- searchTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
- dispatch_async(dispatch_get_main_queue(), ^{
- // Search bar placeholder
- searchTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Search", nil)
- attributes:@{NSForegroundColorAttributeName:[[NCAppBranding themeTextColor] colorWithAlphaComponent:0.5]}];
- // Search bar search icon
- UIImageView *searchImageView = (UIImageView *)searchTextField.leftView;
- searchImageView.image = [searchImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
- [searchImageView setTintColor:[[NCAppBranding themeTextColor] colorWithAlphaComponent:0.5]];
- // Search bar search clear button
- UIImage *clearButtonImage = [clearButton.imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
- [clearButton setImage:clearButtonImage forState:UIControlStateNormal];
- [clearButton setImage:clearButtonImage forState:UIControlStateHighlighted];
- [clearButton setTintColor:[NCAppBranding themeTextColor]];
- });
- self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
- // Contacts placeholder view
- _participantsBackgroundView = [[PlaceholderView alloc] init];
- [_participantsBackgroundView setImage:[UIImage imageNamed:@"contacts-placeholder"]];
- [_participantsBackgroundView.placeholderTextView setText:NSLocalizedString(@"No participants found", nil)];
- [_participantsBackgroundView.placeholderView setHidden:YES];
- [_participantsBackgroundView.loadingView startAnimating];
- self.tableView.backgroundView = _participantsBackgroundView;
-
- // We want ourselves to be the delegate for the result table so didSelectRowAtIndexPath is called for both tables.
- _resultTableViewController.tableView.delegate = self;
- _searchController.delegate = self;
- _searchController.searchBar.delegate = self;
- _searchController.hidesNavigationBarDuringPresentation = NO;
- [self updateCounter];
- self.definesPresentationContext = YES;
-
- UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
- target:self action:@selector(cancelButtonPressed)];
- self.navigationController.navigationBar.topItem.leftBarButtonItem = cancelButton;
- self.navigationItem.title = NSLocalizedString(@"Add participants", nil);
- [self.navigationController.navigationBar setTitleTextAttributes:
- @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]}];
- self.navigationController.navigationBar.tintColor = [NCAppBranding themeTextColor];
- self.navigationController.navigationBar.translucent = NO;
- self.navigationController.navigationBar.barTintColor = [NCAppBranding themeColor];
-
- // Fix uisearchcontroller animation
- self.extendedLayoutIncludesOpaqueBars = YES;
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- - (void)viewDidAppear:(BOOL)animated
- {
- [super viewDidAppear:animated];
-
- self.navigationItem.hidesSearchBarWhenScrolling = NO;
- [self getPossibleParticipants];
- }
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
- self.navigationItem.hidesSearchBarWhenScrolling = NO;
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- }
- #pragma mark - View controller actions
- - (void)cancelButtonPressed
- {
- [self close];
- }
- - (void)close
- {
- if ([self.delegate respondsToSelector:@selector(addParticipantsTableViewControllerDidFinish:)]) {
- [self.delegate addParticipantsTableViewControllerDidFinish:self];
- }
- [self.navigationController dismissViewControllerAnimated:YES completion:nil];
- }
- - (void)addButtonPressed
- {
- if (_room && _selectedParticipants.count > 0) {
- dispatch_group_t addParticipantsGroup = dispatch_group_create();
- [self showAddingParticipantsView];
- for (NCUser *participant in _selectedParticipants) {
- [self addParticipantToRoom:participant withDispatchGroup:addParticipantsGroup];
- }
- dispatch_group_notify(addParticipantsGroup, dispatch_get_main_queue(), ^{
- [self removeAddingParticipantsView];
- if (!self->_errorAddingParticipants) {
- [self close];
- }
- // Reset flag once adding participants process has finished
- self->_errorAddingParticipants = NO;
- });
- } else if ([self.delegate respondsToSelector:@selector(addParticipantsTableViewController:wantsToAdd:)]) {
- [self.delegate addParticipantsTableViewController:self wantsToAdd:_selectedParticipants];
- [self close];
- }
- }
- - (void)addParticipantToRoom:(NCUser *)participant withDispatchGroup:(dispatch_group_t)dispatchGroup
- {
- dispatch_group_enter(dispatchGroup);
- [[NCAPIController sharedInstance] addParticipant:participant.userId ofType:participant.source toRoom:_room.token forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) {
- if (error) {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:NSLocalizedString(@"Could not add participant", nil)
- message:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while adding %@ to the room", nil), participant.name]
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:nil];
-
- [alert addAction:okButton];
- self->_errorAddingParticipants = YES;
- [[NCUserInterfaceController sharedInstance] presentAlertViewController:alert];
- }
- dispatch_group_leave(dispatchGroup);
- }];
- }
- - (void)updateCounter
- {
- UIBarButtonItem *addButton = nil;
- if (!_room) {
- addButton = [[UIBarButtonItem alloc]
- initWithBarButtonSystemItem:UIBarButtonSystemItemDone
- target:self
- action:@selector(addButtonPressed)];
- } else if (_selectedParticipants.count > 0) {
- addButton = [[UIBarButtonItem alloc]
- initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Add (%lu)", nil), (unsigned long)_selectedParticipants.count]
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(addButtonPressed)];
- }
- self.navigationController.navigationBar.topItem.rightBarButtonItem = addButton;
- }
- - (void)showAddingParticipantsView
- {
- [_addingParticipantsIndicator startAnimating];
- UIBarButtonItem *addingParticipantButton = [[UIBarButtonItem alloc] initWithCustomView:_addingParticipantsIndicator];
- self.navigationItem.rightBarButtonItems = @[addingParticipantButton];
- self.tableView.allowsSelection = NO;
- _resultTableViewController.tableView.allowsSelection = NO;
- }
- - (void)removeAddingParticipantsView
- {
- [_addingParticipantsIndicator stopAnimating];
- [self updateCounter];
- self.tableView.allowsSelection = YES;
- _resultTableViewController.tableView.allowsSelection = YES;
- }
- #pragma mark - Participants actions
- - (NSMutableArray *)filterContacts:(NSMutableArray *)contacts
- {
- NSMutableArray *participants = [[NSMutableArray alloc] init];
- for (NCUser *user in contacts) {
- if (![_participantsInRoom containsObject:user.userId]) {
- [participants addObject:user];
- } else if (![user.source isEqualToString:kParticipantTypeUser]) {
- [participants addObject:user];
- }
- }
- return participants;
- }
- - (void)getPossibleParticipants
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCAPIController sharedInstance] getContactsForAccount:activeAccount forRoom:_room.token groupRoom:YES withSearchParam:nil andCompletionBlock:^(NSArray *indexes, NSMutableDictionary *contacts, NSMutableArray *contactList, NSError *error) {
- if (!error) {
- NSMutableArray *storedContacts = [NCContact contactsForAccountId:activeAccount.accountId contains:nil];
- NSMutableArray *combinedContactList = [NCUser combineUsersArray:storedContacts withUsersArray:contactList];
- NSMutableArray *filteredParticipants = [self filterContacts:combinedContactList];
- NSMutableDictionary *participants = [NCUser indexedUsersFromUsersArray:filteredParticipants];
- self->_participants = participants;
- self->_indexes = [[participants allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
- [self->_participantsBackgroundView.loadingView stopAnimating];
- [self->_participantsBackgroundView.loadingView setHidden:YES];
- [self->_participantsBackgroundView.placeholderView setHidden:(participants.count > 0)];
- [self.tableView reloadData];
- } else {
- NSLog(@"Error while trying to get participants: %@", error);
- }
- }];
- }
- - (void)searchForParticipantsWithString:(NSString *)searchString
- {
- [_searchParticipantsTask cancel];
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- _searchParticipantsTask = [[NCAPIController sharedInstance] getContactsForAccount:[[NCDatabaseManager sharedInstance] activeAccount] forRoom:_room.token groupRoom:YES withSearchParam:searchString andCompletionBlock:^(NSArray *indexes, NSMutableDictionary *contacts, NSMutableArray *contactList, NSError *error) {
- if (!error) {
- NSMutableArray *storedContacts = [NCContact contactsForAccountId:activeAccount.accountId contains:searchString];
- NSMutableArray *combinedContactList = [NCUser combineUsersArray:storedContacts withUsersArray:contactList];
- NSMutableArray *filteredParticipants = [self filterContacts:combinedContactList];
- NSMutableDictionary *participants = [NCUser indexedUsersFromUsersArray:filteredParticipants];
- NSArray *sortedIndexes = [[participants allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
- [self->_resultTableViewController setSearchResultContacts:participants withIndexes:sortedIndexes];
- } else {
- if (error.code != -999) {
- NSLog(@"Error while searching for participants: %@", error);
- }
- }
- }];
- }
- - (BOOL)isParticipantAlreadySelected:(NCUser *)participant
- {
- for (NCUser *user in _selectedParticipants) {
- if ([user.userId isEqualToString:participant.userId] &&
- [user.source isEqualToString:participant.source]) {
- return YES;
- }
- }
- return NO;
- }
- - (void)removeSelectedParticipant:(NCUser *)participant
- {
- NCUser *userToDelete = nil;
- for (NCUser *user in _selectedParticipants) {
- if ([user.userId isEqualToString:participant.userId] &&
- [user.source isEqualToString:participant.source]) {
- userToDelete = user;
- }
- }
-
- if (userToDelete) {
- [_selectedParticipants removeObject:userToDelete];
- }
- }
- #pragma mark - Search controller
- - (void)updateSearchResultsForSearchController:(UISearchController *)searchController
- {
- [_searchTimer invalidate];
- _searchTimer = nil;
- [_resultTableViewController showSearchingUI];
- dispatch_async(dispatch_get_main_queue(), ^{
- self->_searchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(searchForParticipants) userInfo:nil repeats:NO];
- });
- }
- - (void)searchForParticipants
- {
- NSString *searchString = _searchController.searchBar.text;
- if (![searchString isEqualToString:@""]) {
- [self searchForParticipantsWithString:searchString];
- }
- }
- - (void)didDismissSearchController:(UISearchController *)searchController
- {
- [self.tableView reloadData];
- }
- #pragma mark - Table view data source
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- return _indexes.count;
- }
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- NSString *index = [_indexes objectAtIndex:section];
- NSArray *participants = [_participants objectForKey:index];
- return participants.count;
- }
- - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
- {
- return kContactsTableCellHeight;
- }
- - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
- {
- return [_indexes objectAtIndex:section];
- }
- - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
- {
- return _indexes;
- }
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- {
- NSString *index = [_indexes objectAtIndex:indexPath.section];
- NSArray *participants = [_participants objectForKey:index];
- NCUser *participant = [participants objectAtIndex:indexPath.row];
- ContactsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kContactCellIdentifier forIndexPath:indexPath];
- if (!cell) {
- cell = [[ContactsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kContactCellIdentifier];
- }
-
- cell.labelTitle.text = participant.name;
- [cell.contactImage setActorAvatarForId:participant.userId withType:participant.source withDisplayName:participant.name withRoomToken:_room.token];
- UIImage *selectionImage = [UIImage systemImageNamed:@"circle"];
- UIColor *selectionImageColor = [UIColor tertiaryLabelColor];
- if ([self isParticipantAlreadySelected:participant]) {
- selectionImage = [UIImage systemImageNamed:@"checkmark.circle.fill"];
- selectionImageColor = [NCAppBranding elementColor];
- }
- UIImageView *selectionImageView = [[UIImageView alloc] initWithImage:selectionImage];
- selectionImageView.tintColor = selectionImageColor;
- cell.accessoryView = selectionImageView;
- return cell;
- }
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- {
- NSString *index = nil;
- NSArray *participants = nil;
-
- if (_searchController.active && _resultTableViewController.contacts.count > 0) {
- index = [_resultTableViewController.indexes objectAtIndex:indexPath.section];
- participants = [_resultTableViewController.contacts objectForKey:index];
- } else {
- index = [_indexes objectAtIndex:indexPath.section];
- participants = [_participants objectForKey:index];
- }
-
- NCUser *participant = [participants objectAtIndex:indexPath.row];
- if (![self isParticipantAlreadySelected:participant]) {
- [_selectedParticipants addObject:participant];
- } else {
- [self removeSelectedParticipant:participant];
- }
-
- _resultTableViewController.selectedParticipants = _selectedParticipants;
-
- [tableView beginUpdates];
- [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
- [tableView endUpdates];
-
- [self updateCounter];
- }
- @end
|