#import "TGGroupAdminsController.h" #import "ActionStage.h" #import "TGGroupManagementSignals.h" #import "TGDatabase.h" #import "TGTelegraph.h" #import "TGSwitchCollectionItem.h" #import "TGCommentCollectionItem.h" #import "TGGroupInfoUserCollectionItem.h" #import "TGSearchBar.h" #import "TGSearchDisplayMixin.h" #import "TGBotUserInfoController.h" #import "TGTelegraphUserInfoController.h" @interface TGGroupAdminsController () { int64_t _peerId; TGConversation *_conversation; NSSet *_referenceChatAdminUids; TGCollectionMenuSection *_allMembersSection; TGSwitchCollectionItem *_allMembersSwitchItem; TGCommentCollectionItem *_allMembersCommentItem; TGCollectionMenuSection *_usersSection; UIView *_progressView; SMetaDisposable *_toggleAdminsDisposable; UIView *_searchBarOverlay; UIBarButtonItem *_searchButtonItem; UIView *_searchReferenceView; UIView *_searchBarWrapper; TGSearchBar *_searchBar; NSString *_filterText; } @property (nonatomic, strong) ASHandle *actionHandle; @end @implementation TGGroupAdminsController - (instancetype)initWithPeerId:(int64_t)peerId { self = [super init]; if (self != nil) { _peerId = peerId; self.title = TGLocalized(@"ChatAdmins.Title"); _searchButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchPressed)]; _actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true]; _toggleAdminsDisposable = [[SMetaDisposable alloc] init]; _allMembersSwitchItem = [[TGSwitchCollectionItem alloc] initWithTitle:TGLocalized(@"ChatAdmins.AllMembersAreAdmins") isOn:true]; __weak TGGroupAdminsController *weakSelf = self; _allMembersSwitchItem.toggled = ^(bool value) { __strong TGGroupAdminsController *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf toggleAllMembersAreAdmins:value]; } }; _allMembersCommentItem = [[TGCommentCollectionItem alloc] initWithFormattedText:TGLocalized(@"ChatAdmins.AllMembersAreAdminsOnHelp")]; _allMembersSection = [[TGCollectionMenuSection alloc] initWithItems:@[_allMembersSwitchItem, _allMembersCommentItem]]; _allMembersSection.insets = UIEdgeInsetsMake(35.0, 0.0, 31.0, 0.0); [self.menuSections addSection:_allMembersSection]; _usersSection = [[TGCollectionMenuSection alloc] init]; [self.menuSections addSection:_usersSection]; TGConversation *conversation = [TGDatabaseInstance() loadConversationWithId:_peerId]; _referenceChatAdminUids = conversation.chatParticipants.chatAdminUids; [self setConversation:conversation]; [ActionStageInstance() watchForPaths:@[ [[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _peerId], @"/tg/userdatachanges", @"/tg/userpresencechanges", @"/as/updateRelativeTimestamps" ] watcher:self]; [ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversationExtended/(%lld)", _peerId] options:@{@"conversationId": @(_peerId)} watcher:TGTelegraphInstance]; } return self; } - (void)dealloc { [ActionStageInstance() removeWatcher:self]; [_toggleAdminsDisposable dispose]; } - (void)loadView { [super loadView]; _progressView = [[UIView alloc] initWithFrame:self.view.bounds]; _progressView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.4f]; _progressView.alpha = 0.0f; [self.view addSubview:_progressView]; _searchBarOverlay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.navigationController.view.frame.size.width, 63)]; _searchBarOverlay.alpha = 0.0f; _searchBarOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth; _searchBarOverlay.backgroundColor = [UIColor whiteColor]; _searchBarOverlay.userInteractionEnabled = false; _searchBarWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, -64, self.view.frame.size.width, 63)]; _searchBarWrapper.clipsToBounds = true; _searchBarWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth; _searchBarWrapper.backgroundColor = [UIColor whiteColor]; _searchBar = [[TGSearchBar alloc] initWithFrame:CGRectMake(0.0f, 20, _searchBarWrapper.frame.size.width, [TGSearchBar searchBarBaseHeight]) style:TGSearchBarStyleLightPlain]; _searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; _searchBar.customBackgroundView.image = nil; _searchBar.customActiveBackgroundView.image = nil; _searchBar.delegate = self; [_searchBar setShowsCancelButton:true animated:false]; [_searchBar setAlwaysExtended:true]; _searchBar.placeholder = TGLocalized(@"Common.Search"); [_searchBar sizeToFit]; _searchBar.delayActivity = false; [_searchBarWrapper addSubview:_searchBar]; _searchReferenceView = [[UIView alloc] initWithFrame:self.view.bounds]; _searchReferenceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _searchReferenceView.userInteractionEnabled = false; [self.view addSubview:_searchReferenceView]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController.view addSubview:_searchBarWrapper]; [UIView animateWithDuration:0.3 animations:^{ _searchBarWrapper.alpha = 1.0f; }]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [UIView animateWithDuration:0.3 animations:^{ _searchBarWrapper.alpha = 0.0f; } completion:^(BOOL finished) { if (finished) { [_searchBarWrapper removeFromSuperview]; } }]; } - (void)setInProgress:(bool)inProgress { [UIView animateWithDuration:0.4 delay:0.2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ _progressView.alpha = inProgress ? 1.0f : 0.0f; } completion:nil]; } - (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)__unused arguments { if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _peerId]]) { [self setConversation:((SGraphObjectNode *)resource).object]; } } - (void)setConversation:(TGConversation *)conversation { NSMutableArray *users = [[NSMutableArray alloc] init]; for (NSNumber *nUid in conversation.chatParticipants.chatParticipantUids) { TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]]; if (user != nil) { [users addObject:user]; } } TGDispatchOnMainThread(^{ [self setConversation:conversation andUsers:users]; }); } - (NSArray *)sortedUsers:(NSArray *)users chatAdminUids:(NSSet *)chatAdminUids { int32_t selfUid = TGTelegraphInstance.clientUserId; return [users sortedArrayUsingComparator:^NSComparisonResult(TGUser *user1, TGUser *user2) { if (user1.botKind != user2.botKind) { return user1.botKind < user2.botKind ? NSOrderedAscending : NSOrderedDescending; } if (user1.kind != user2.kind) { return user1.kind < user2.kind ? NSOrderedAscending : NSOrderedDescending; } if (user1.uid == selfUid) { return NSOrderedAscending; } else if (user2.uid == selfUid) { return NSOrderedDescending; } bool isAdmin1 = [chatAdminUids containsObject:@(user1.uid)]; bool isAdmin2 = [chatAdminUids containsObject:@(user2.uid)]; if (isAdmin1 != isAdmin2) { if (isAdmin1) { return NSOrderedAscending; } else { return NSOrderedDescending; } } if (user1.presence.online != user2.presence.online) return user1.presence.online ? NSOrderedAscending : NSOrderedDescending; if ((user1.presence.lastSeen < 0) != (user2.presence.lastSeen < 0)) return user1.presence.lastSeen >= 0 ? NSOrderedAscending : NSOrderedDescending; return user1.presence.lastSeen > user2.presence.lastSeen ? NSOrderedAscending : NSOrderedDescending; }]; } - (void)setConversation:(TGConversation *)conversation andUsers:(NSArray *)users { _conversation = conversation; NSArray *filteredUsers = users; if (_filterText.length != 0) { NSMutableArray *result = [[NSMutableArray alloc] init]; NSString *query = [_filterText lowercaseString]; for (TGUser *user in users) { if ([[user.firstName lowercaseString] hasPrefix:query] || [[user.lastName lowercaseString] hasPrefix:query]) { [result addObject:user]; } } filteredUsers = result; } if (_conversation.hasAdmins) { if (self.navigationItem.rightBarButtonItem != _searchButtonItem) { [self setRightBarButtonItem:_searchButtonItem]; } } else { if (self.navigationItem.rightBarButtonItem == _searchButtonItem) { [self setRightBarButtonItem:nil]; } } _allMembersSwitchItem.isOn = !_conversation.hasAdmins; _allMembersCommentItem.text = !_conversation.hasAdmins ? TGLocalized(@"ChatAdmins.AllMembersAreAdminsOnHelp") : TGLocalized(@"ChatAdmins.AllMembersAreAdminsOffHelp"); NSMutableSet *currentUserIds = [[NSMutableSet alloc] init]; for (TGGroupInfoUserCollectionItem *userItem in _usersSection.items) { [currentUserIds addObject:@(userItem.user.uid)]; } NSMutableSet *updatedUserIds = [[NSMutableSet alloc] init]; for (TGUser *user in filteredUsers) { [updatedUserIds addObject:@(user.uid)]; } if ([currentUserIds isEqualToSet:updatedUserIds]) { NSArray *sortedUsers = [self sortedUsers:filteredUsers chatAdminUids:_referenceChatAdminUids]; NSUInteger sectionIndex = [self indexForSection:_usersSection]; NSInteger index = -1; for (TGUser *user in sortedUsers) { index++; NSInteger currentIndex = -1; for (TGGroupInfoUserCollectionItem *userItem in _usersSection.items) { currentIndex++; if (userItem.user.uid == user.uid) { if (currentIndex != index) { [_usersSection deleteItemAtIndex:currentIndex]; [_usersSection insertItem:userItem atIndex:index]; [self.collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:currentIndex inSection:sectionIndex] toIndexPath:[NSIndexPath indexPathForItem:index inSection:sectionIndex]]; } break; } } } [self updateItemPositions]; for (TGGroupInfoUserCollectionItem *userItem in _usersSection.items) { userItem.selectable = false;//userItem.user.uid != TGTelegraphInstance.clientUserId; userItem.displaySwitch = true; if (userItem.user.uid == TGTelegraphInstance.clientUserId) { userItem.enableSwitch = false; userItem.switchIsOn = true; userItem.customStatus = TGLocalized(@"ChatAdmins.AdminLabel"); } else { userItem.enableSwitch = conversation.hasAdmins; if (conversation.hasAdmins) { bool isAdmin = [conversation.chatParticipants.chatAdminUids containsObject:@(userItem.user.uid)]; userItem.switchIsOn = isAdmin; userItem.customStatus = isAdmin ? TGLocalized(@"ChatAdmins.AdminLabel") : nil; } else { userItem.switchIsOn = !conversation.hasAdmins; userItem.customStatus = nil; } } } } else { while (_usersSection.items.count != 0) { [_usersSection deleteItemAtIndex:0]; } __weak TGGroupAdminsController *weakSelf = self; for (TGUser *user in [self sortedUsers:filteredUsers chatAdminUids:_referenceChatAdminUids]) { TGGroupInfoUserCollectionItem *userItem = [[TGGroupInfoUserCollectionItem alloc] init]; userItem.interfaceHandle = _actionHandle; userItem.selectable = false;//user.uid != TGTelegraphInstance.clientUserId; userItem.displaySwitch = true; if (user.uid == TGTelegraphInstance.clientUserId) { userItem.enableSwitch = false; userItem.switchIsOn = true; userItem.customStatus = TGLocalized(@"ChatAdmins.AdminLabel"); } else { userItem.enableSwitch = conversation.hasAdmins; if (conversation.hasAdmins) { bool isAdmin = [conversation.chatParticipants.chatAdminUids containsObject:@(user.uid)]; userItem.switchIsOn = isAdmin; userItem.customStatus = isAdmin ? TGLocalized(@"ChatAdmins.AdminLabel") : nil; } else { userItem.switchIsOn = !conversation.hasAdmins; userItem.customStatus = nil; } userItem.toggled = ^(bool value) { __strong TGGroupAdminsController *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf toggleUserIsAdmin:user isAdmin:value]; } }; } [userItem setCanEdit:false]; [userItem setUser:user]; [_usersSection addItem:userItem]; } [self.collectionView reloadData]; } } - (void)toggleAllMembersAreAdmins:(bool)allMembersAreAdmins { [self setInProgress:true]; __weak TGGroupAdminsController *weakSelf = self; [_toggleAdminsDisposable setDisposable:[[[TGGroupManagementSignals toggleGroupHasAdmins:_peerId hasAdmins:!allMembersAreAdmins] onDispose:^{ TGDispatchOnMainThread(^{ __strong TGGroupAdminsController *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf setInProgress:false]; } }); }] startWithNext:nil]]; } - (void)toggleUserIsAdmin:(TGUser *)user isAdmin:(bool)isAdmin { [self setInProgress:true]; __weak TGGroupAdminsController *weakSelf = self; [_toggleAdminsDisposable setDisposable:[[[TGGroupManagementSignals toggleUserIsAdmin:_peerId user:user isAdmin:isAdmin] onDispose:^{ TGDispatchOnMainThread(^{ __strong TGGroupAdminsController *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf setInProgress:false]; } }); }] startWithNext:nil]]; } - (void)searchPressed { [self setSearchHidden:false animated:true]; [_searchBar becomeFirstResponder]; } - (void)setSearchHidden:(bool)hidden animated:(bool)animated { void (^changeBlock)(void) = ^ { CGRect frame = _searchBarWrapper.frame; if (hidden) { frame.origin.y = -64; _searchBarOverlay.alpha = 0.0f; } else { frame.origin.y = 0; if (self.navigationController.modalPresentationStyle == UIModalPresentationFormSheet) frame.origin.y -= 20; _searchBarOverlay.alpha = 1.0f; } _searchBarWrapper.frame = frame; }; if (animated) [UIView animateWithDuration:0.2f animations:changeBlock]; else changeBlock(); } - (void)searchBar:(TGSearchBar *)__unused searchBar willChangeHeight:(CGFloat)__unused newHeight { } - (void)searchBar:(UISearchBar *)__unused searchBar textDidChange:(NSString *)searchText { if (!TGStringCompare(searchText, _filterText)) { _filterText = searchText; [self setConversation:_conversation]; } } - (void)searchBarTextDidBeginEditing:(UISearchBar *)__unused searchBar { [self setSearchHidden:false animated:true]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)__unused searchBar { [_searchBar resignFirstResponder]; _searchBar.text = @""; if (_filterText.length != 0) { _filterText = nil; [self setConversation:_conversation]; } [self setSearchHidden:true animated:true]; } - (void)actionStageActionRequested:(NSString *)action options:(id)options { if ([action isEqualToString:@"openUser"]) { int32_t uid = [options[@"uid"] int32Value]; if (uid != 0) { TGUser *user = [TGDatabaseInstance() loadUser:uid]; if (user.kind == TGUserKindBot || user.kind == TGUserKindSmartBot) { TGBotUserInfoController *userInfoController = [[TGBotUserInfoController alloc] initWithUid:uid sendCommand:nil]; [self.navigationController pushViewController:userInfoController animated:true]; } else { TGTelegraphUserInfoController *userInfoController = [[TGTelegraphUserInfoController alloc] initWithUid:uid]; [self.navigationController pushViewController:userInfoController animated:true]; } } } } @end