1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGGroupInfoController.m
2016-02-25 01:03:51 +01:00

1659 lines
68 KiB
Objective-C

/*
* This is the source code of Telegram for iOS v. 1.1
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Peter Iakovlev, 2013.
*/
#import "TGGroupInfoController.h"
#import "ActionStage.h"
#import "SGraphObjectNode.h"
#import "TGConversation.h"
#import "TGDatabase.h"
#import "TGHacks.h"
#import "TGFont.h"
#import "TGStringUtils.h"
#import "UIDevice+PlatformInfo.h"
#import "TGAppDelegate.h"
#import "TGTelegraph.h"
#import "TGTelegramNetworking.h"
#import "TGInterfaceManager.h"
#import "TGNavigationBar.h"
#import "TGTelegraphDialogListCompanion.h"
#import "TGConversationChangeTitleRequestActor.h"
#import "TGConversationChangePhotoActor.h"
#import "TGHeaderCollectionItem.h"
#import "TGSwitchCollectionItem.h"
#import "TGVariantCollectionItem.h"
#import "TGButtonCollectionItem.h"
#import "TGCommentCollectionItem.h"
#import "TGGroupInfoCollectionItem.h"
#import "TGGroupInfoUserCollectionItem.h"
#import "TGTelegraphUserInfoController.h"
#import "TGGroupInfoSelectContactController.h"
#import "TGBotUserInfoController.h"
#import "TGAlertSoundController.h"
#import "TGRemoteImageView.h"
#import "TGImageUtils.h"
#import "TGAlertView.h"
#import "TGActionSheet.h"
#import "TGModernGalleryController.h"
#import "TGGroupAvatarGalleryItem.h"
#import "TGGroupAvatarGalleryModel.h"
#import "TGOverlayControllerWindow.h"
#import "TGSharedMediaController.h"
#import "TGTimerTarget.h"
#import "TGGroupManagementSignals.h"
#import "TGProgressWindow.h"
#import "TGGroupInfoShareLinkController.h"
#import "TGGroupAdminsController.h"
#import "TGMediaAvatarMenuMixin.h"
@interface TGGroupInfoController () <TGGroupInfoSelectContactControllerDelegate, TGAlertSoundControllerDelegate>
{
bool _editing;
bool _haveEditableUsers;
int64_t _conversationId;
TGConversation *_conversation;
TGCollectionMenuSection *_groupInfoSection;
TGGroupInfoCollectionItem *_groupInfoItem;
TGButtonCollectionItem *_setGroupPhotoItem;
TGCollectionMenuSection *_notificationsAndMediaSection;
TGSwitchCollectionItem *_notificationsItem;
TGVariantCollectionItem *_chatAdminsItem;
TGVariantCollectionItem *_sharedMediaItem;
TGVariantCollectionItem *_soundItem;
TGCollectionMenuSection *_usersSection;
TGHeaderCollectionItem *_usersSectionHeader;
TGCommentCollectionItem *_usersSectionUpgradeNotice1;
TGCommentCollectionItem *_usersSectionUpgradeNotice2;
TGButtonCollectionItem *_addParticipantItem;
NSMutableDictionary *_groupNotificationSettings;
NSInteger _sharedMediaCount;
NSMutableArray *_soonToBeAddedUserIds;
NSMutableArray *_soonToBeRemovedUserIds;
UILabel *_leftLabel;
NSTimer *_muteExpirationTimer;
int _upgradeNoticeMemberLimit;
bool _ignoreUpdates;
TGMediaAvatarMenuMixin *_avatarMixin;
}
@end
@implementation TGGroupInfoController
- (instancetype)initWithConversationId:(int64_t)conversationId
{
self = [super init];
if (self != nil)
{
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Back") style:UIBarButtonItemStylePlain target:self action:@selector(backPressed)];
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
_soonToBeAddedUserIds = [[NSMutableArray alloc] init];
_soonToBeRemovedUserIds = [[NSMutableArray alloc] init];
_conversationId = conversationId;
_groupNotificationSettings = [[NSMutableDictionary alloc] initWithDictionary:@{@"muteUntil": @(0), @"soundId": @(1)}];
[self setTitleText:TGLocalized(@"GroupInfo.Title")];
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Edit") style:UIBarButtonItemStylePlain target:self action:@selector(editPressed)] animated:false];
_groupInfoItem = [[TGGroupInfoCollectionItem alloc] init];
_groupInfoItem.interfaceHandle = _actionHandle;
_setGroupPhotoItem = [[TGButtonCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.SetGroupPhoto") action:@selector(setGroupPhotoPressed)];
_setGroupPhotoItem.titleColor = TGAccentColor();
_setGroupPhotoItem.deselectAutomatically = true;
_groupInfoSection = [[TGCollectionMenuSection alloc] initWithItems:@[
_groupInfoItem,
_setGroupPhotoItem
]];
[self.menuSections addSection:_groupInfoSection];
_notificationsItem = [[TGSwitchCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.Notifications") isOn:false];
__weak TGGroupInfoController *weakSelf = self;
_notificationsItem.toggled = ^(bool value) {
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf _commitEnableNotifications:value orMuteFor:0];
}
};
_notificationsItem.deselectAutomatically = true;
_soundItem = [[TGVariantCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.Sound") variant:nil action:@selector(soundPressed)];
_soundItem.deselectAutomatically = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
_sharedMediaItem = [[TGVariantCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.SharedMedia") variant:nil action:@selector(sharedMediaPressed)];
_chatAdminsItem = [[TGVariantCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.ChatAdmins") action:@selector(chatAdminsPressed)];
_notificationsAndMediaSection = [[TGCollectionMenuSection alloc] initWithItems:@[
_notificationsItem,
_sharedMediaItem
]];
UIEdgeInsets notificationsAndMediaSectionInsets = _notificationsAndMediaSection.insets;
notificationsAndMediaSectionInsets.bottom = 18.0f;
_notificationsAndMediaSection.insets = notificationsAndMediaSectionInsets;
[self.menuSections addSection:_notificationsAndMediaSection];
_usersSectionHeader = [[TGHeaderCollectionItem alloc] initWithTitle:@""];
_addParticipantItem = [[TGButtonCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.AddParticipant") action:@selector(addParticipantPressed)];
_addParticipantItem.leftInset = 65.0f;
_addParticipantItem.icon = [UIImage imageNamed:@"GroupInfoIconAddMember.png"];
_addParticipantItem.titleColor = TGAccentColor();
_addParticipantItem.deselectAutomatically = true;
_usersSection = [[TGCollectionMenuSection alloc] initWithItems:@[
_usersSectionHeader,
_addParticipantItem
]];
[self.menuSections addSection:_usersSection];
_usersSectionUpgradeNotice1 = [[TGCommentCollectionItem alloc] initWithFormattedText:TGLocalized(@"Group.UpgradeNoticeText1")];
_usersSectionUpgradeNotice1.topInset = -2.0f;
int channelGroupMemberLimit = 1000;
NSData *data = [TGDatabaseInstance() customProperty:@"maxChannelGroupMembers"];
if (data.length >= 4)
{
int32_t maxChannelGroupMembers = 0;
[data getBytes:&maxChannelGroupMembers length:4];
if (maxChannelGroupMembers != 0) {
channelGroupMemberLimit = MAX(0, maxChannelGroupMembers);
}
}
data = [TGDatabaseInstance() customProperty:@"maxChatParticipants"];
if (data.length >= 4)
{
int32_t maxChatParticipants = 0;
[data getBytes:&maxChatParticipants length:4];
if (maxChatParticipants == 0) {
_upgradeNoticeMemberLimit = 99;
} else {
_upgradeNoticeMemberLimit = maxChatParticipants - 1;
}
}
#if TARGET_IPHONE_SIMULATOR
_upgradeNoticeMemberLimit = 4;
#endif
_usersSectionUpgradeNotice2 = [[TGCommentCollectionItem alloc] initWithFormattedText:[TGLocalized(@"Group.UpgradeNoticeText2") stringByReplacingOccurrencesOfString:@"{supergroup_member_limit}" withString:[[NSString alloc] initWithFormat:@"%d", channelGroupMemberLimit]]];
_usersSectionUpgradeNotice2.topInset = -5.0f;
_usersSectionUpgradeNotice2.bottomInset = 5.0f;
TGButtonCollectionItem *leaveGroupItem = [[TGButtonCollectionItem alloc] initWithTitle:TGLocalized(@"GroupInfo.DeleteAndExit") action:@selector(leaveGroupPressed)];
leaveGroupItem.titleColor = TGDestructiveAccentColor();
leaveGroupItem.alignment = NSTextAlignmentCenter;
leaveGroupItem.deselectAutomatically = true;
[self.menuSections addSection:[[TGCollectionMenuSection alloc] initWithItems:@[
leaveGroupItem
]]];
[self _loadUsersAndUpdateConversation:[TGDatabaseInstance() loadConversationWithIdCached:_conversationId]];
[self _updateNotificationItems:false];
[self _updateSharedMediaCount];
[self resetSections];
[ActionStageInstance() dispatchOnStageQueue:^
{
[ActionStageInstance() watchForPaths:@[
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId],
@"/tg/userdatachanges",
@"/tg/userpresencechanges",
@"/as/updateRelativeTimestamps",
[[NSString alloc] initWithFormat:@"/tg/sharedMediaCount/(%" PRIx64 ")", (int64_t)_conversationId]
] watcher:self];
[ActionStageInstance() watchForPath:[NSString stringWithFormat:@"/tg/peerSettings/(%" PRId64 ")", _conversationId] watcher:self];
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/peerSettings/(%" PRId64 ",cachedOnly)", _conversationId] options:@{@"peerId": @(_conversationId)} watcher:self];
NSArray *addActions = [ActionStageInstance() rejoinActionsWithGenericPathNow:@"/tg/conversation/@/addMember/@" prefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")", _conversationId] watcher:self];
NSArray *deleteActions = [ActionStageInstance() rejoinActionsWithGenericPathNow:@"/tg/conversation/@/deleteMember/@" prefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")", _conversationId] watcher:self];
NSArray *changeTitleActions = [ActionStageInstance() rejoinActionsWithGenericPathNow:@"/tg/conversation/@/changeTitle/@" prefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")", _conversationId] watcher:self];
NSArray *changeAvatarActions = [ActionStageInstance() rejoinActionsWithGenericPathNow:@"/tg/conversation/@/updateAvatar/@" prefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")", _conversationId] watcher:self];
NSString *updatingTitle = nil;
if (changeTitleActions.count != 0)
{
NSString *action = [changeTitleActions lastObject];
TGConversationChangeTitleRequestActor *actor = (TGConversationChangeTitleRequestActor *)[ActionStageInstance() executingActorWithPath:action];
if (actor != nil)
updatingTitle = actor.currentTitle;
}
UIImage *updatingAvatar = nil;
bool haveUpdatingAvatar = false;
if (changeAvatarActions.count != 0)
{
NSString *action = [changeAvatarActions lastObject];
TGConversationChangePhotoActor *actor = (TGConversationChangePhotoActor *)[ActionStageInstance() executingActorWithPath:action];
if (actor != nil)
{
updatingAvatar = actor.currentImage;
haveUpdatingAvatar = true;
}
}
if (addActions.count != 0 || deleteActions.count != 0 || changeTitleActions.count != 0 || changeAvatarActions.count != 0)
{
TGDispatchOnMainThread(^
{
for (NSString *path in addActions)
{
NSRange range = [path rangeOfString:@"/addMember/("];
int32_t uid = (int32_t)[[path substringFromIndex:(range.location + range.length)] intValue];
[_soonToBeAddedUserIds addObject:@(uid)];
}
for (NSString *path in deleteActions)
{
NSRange range = [path rangeOfString:@"/deleteMember/("];
int32_t uid = (int32_t)[[path substringFromIndex:(range.location + range.length)] intValue];
[_soonToBeRemovedUserIds addObject:@(uid)];
}
[_groupInfoItem setUpdatingTitle:updatingTitle];
[_groupInfoItem setUpdatingAvatar:updatingAvatar hasUpdatingAvatar:haveUpdatingAvatar];
[_setGroupPhotoItem setEnabled:!haveUpdatingAvatar];
[self _loadUsersAndUpdateConversation:_conversation];
});
}
}];
}
return self;
}
- (void)resetSections {
bool reload = false;
if (_editing) {
if (_conversation.isCreator) {
if ([_notificationsAndMediaSection indexOfItem:_chatAdminsItem] == NSNotFound) {
[_notificationsAndMediaSection insertItem:_chatAdminsItem atIndex:1];
reload = true;
}
} else {
if ([_notificationsAndMediaSection indexOfItem:_chatAdminsItem] != NSNotFound) {
[_notificationsAndMediaSection deleteItem:_chatAdminsItem];
reload = true;
}
}
}
bool canAddParticipants = _conversation.isCreator || !_conversation.hasAdmins || (_conversation.hasAdmins && _conversation.isAdmin);
bool canEditGroup = canAddParticipants;
if (canAddParticipants) {
if ([_usersSection indexOfItem:_addParticipantItem] == NSNotFound) {
[_usersSection insertItem:_addParticipantItem atIndex:1];
reload = true;
}
} else {
if ([_usersSection deleteItem:_addParticipantItem]) {
reload = true;
}
}
if (canEditGroup) {
if ([_groupInfoSection indexOfItem:_setGroupPhotoItem] == NSNotFound) {
[_groupInfoSection addItem:_setGroupPhotoItem];
reload = true;
}
} else {
if ([_groupInfoSection deleteItem:_setGroupPhotoItem]) {
reload = true;
}
}
if (reload) {
[self.collectionView reloadData];
}
if (!canEditGroup && _editing) {
[self leaveEditingMode:false];
}
if (!(_conversation.kickedFromChat || _conversation.leftChat || _conversation.isDeactivated)) {
if (canEditGroup) {
if (self.navigationItem.rightBarButtonItem == nil) {
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Edit") style:UIBarButtonItemStylePlain target:self action:@selector(editPressed)] animated:false];
}
} else {
if (self.navigationItem.rightBarButtonItem != nil) {
[self setRightBarButtonItem:nil];
}
}
}
}
- (void)backPressed
{
[self.navigationController popViewControllerAnimated:true];
}
- (void)dealloc
{
[_actionHandle reset];
[ActionStageInstance() removeWatcher:self];
}
#pragma mark -
- (void)_resetCollectionView
{
[super _resetCollectionView];
[self _updateLeftState];
[self.collectionView setAllowEditingCells:_haveEditableUsers animated:false];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self _layoutLeftLabel:self.interfaceOrientation];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self _layoutLeftLabel:toInterfaceOrientation];
}
#pragma mark -
- (void)editPressed
{
if (!_editing)
{
_editing = true;
[_groupInfoItem setEditing:true animated:true];
NSIndexPath *sharedMediaIndexPath = [self indexPathForItem:_sharedMediaItem];
if (sharedMediaIndexPath != nil)
{
[self.menuSections beginRecordingChanges];
[self.menuSections replaceItemInSection:sharedMediaIndexPath.section atIndex:sharedMediaIndexPath.item withItem:_soundItem];
if (_conversation.isCreator) {
if ([_notificationsAndMediaSection indexOfItem:_chatAdminsItem] == NSNotFound) {
[self.menuSections insertItem:_chatAdminsItem toSection:[self indexForSection:_notificationsAndMediaSection] atIndex:2];
}
}
[self.menuSections commitRecordedChanges:self.collectionView];
}
[self enterEditingMode:true];
}
}
- (void)donePressed
{
if (_editing)
{
_editing = false;
if (!TGStringCompare(_conversation.chatTitle, [_groupInfoItem editingTitle]) && [_groupInfoItem editingTitle] != nil)
[self _commitUpdateTitle:[_groupInfoItem editingTitle]];
[_groupInfoItem setEditing:false animated:true];
}
NSIndexPath *soundIndexPath = [self indexPathForItem:_soundItem];
if (soundIndexPath != nil)
{
[self.menuSections beginRecordingChanges];
[self.menuSections replaceItemInSection:soundIndexPath.section atIndex:soundIndexPath.item withItem:_sharedMediaItem];
if ([_notificationsAndMediaSection indexOfItem:_chatAdminsItem] != NSNotFound) {
[self.menuSections deleteItemFromSection:[self indexForSection:_notificationsAndMediaSection] atIndex:[_notificationsAndMediaSection indexOfItem:_chatAdminsItem]];
}
[self.menuSections commitRecordedChanges:self.collectionView];
}
[self leaveEditingMode:true];
}
- (void)didEnterEditingMode:(bool)animated
{
[super didEnterEditingMode:animated];
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Done") style:UIBarButtonItemStyleDone target:self action:@selector(donePressed)] animated:true];
}
- (void)didLeaveEditingMode:(bool)animated
{
[super didLeaveEditingMode:animated];
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Edit") style:UIBarButtonItemStylePlain target:self action:@selector(editPressed)] animated:animated];
}
- (void)setGroupPhotoPressed
{
if ([_groupInfoSection indexOfItem:_setGroupPhotoItem] == NSNotFound)
return;
__weak TGGroupInfoController *weakSelf = self;
_avatarMixin = [[TGMediaAvatarMenuMixin alloc] initWithParentController:self hasDeleteButton:(_conversation.chatPhotoSmall.length != 0)];
_avatarMixin.didFinishWithImage = ^(UIImage *image)
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _updateGroupProfileImage:image];
strongSelf->_avatarMixin = nil;
};
_avatarMixin.didFinishWithDelete = ^
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _commitDeleteAvatar];
strongSelf->_avatarMixin = nil;
};
_avatarMixin.didDismiss = ^
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_avatarMixin = nil;
};
[_avatarMixin present];
}
- (void)_updateGroupProfileImage:(UIImage *)image
{
if (image == nil)
return;
if (MIN(image.size.width, image.size.height) < 160.0f)
image = TGScaleImageToPixelSize(image, CGSizeMake(160, 160));
NSData *imageData = UIImageJPEGRepresentation(image, 0.6f);
if (imageData == nil)
return;
TGImageProcessor filter = [TGRemoteImageView imageProcessorForName:@"circle:64x64"];
UIImage *avatarImage = filter(image);
[_groupInfoItem setUpdatingAvatar:avatarImage hasUpdatingAvatar:true];
[_setGroupPhotoItem setEnabled:false];
NSMutableDictionary *uploadOptions = [[NSMutableDictionary alloc] init];
[uploadOptions setObject:imageData forKey:@"imageData"];
[uploadOptions setObject:[NSNumber numberWithLongLong:_conversation.conversationId] forKey:@"conversationId"];
[uploadOptions setObject:avatarImage forKey:@"currentImage"];
[ActionStageInstance() dispatchOnStageQueue:^
{
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/updateAvatar/(updateAvatar%d)", _conversationId, actionId] options:uploadOptions watcher:self];
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/updateAvatar/(updateAvatar%d)", _conversationId, actionId++] options:uploadOptions watcher:TGTelegraphInstance];
}];
}
- (void)_commitDeleteAvatar
{
[_groupInfoItem setUpdatingAvatar:nil hasUpdatingAvatar:true];
[_setGroupPhotoItem setEnabled:false];
NSMutableDictionary *uploadOptions = [[NSMutableDictionary alloc] init];
[uploadOptions setObject:[NSNumber numberWithLongLong:_conversation.conversationId] forKey:@"conversationId"];
[ActionStageInstance() dispatchOnStageQueue:^
{
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/updateAvatar/(deleteAvatar%d)", _conversationId, actionId] options:uploadOptions watcher:self];
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/updateAvatar/(deleteAvatar%d)", _conversationId, actionId++] options:uploadOptions watcher:TGTelegraphInstance];
}];
}
- (void)_commitCancelAvatarUpdate
{
[_groupInfoItem setUpdatingAvatar:nil hasUpdatingAvatar:false];
[_setGroupPhotoItem setEnabled:true];
[ActionStageInstance() dispatchOnStageQueue:^
{
NSArray *actors = [ActionStageInstance() executingActorsWithPathPrefix:[NSString stringWithFormat:@"/tg/conversation/(%lld)/updateAvatar/", _conversationId]];
for (ASActor *actor in actors)
{
[ActionStageInstance() removeAllWatchersFromPath:actor.path];
}
}];
}
- (void)notificationsPressed
{
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"UserInfo.NotificationsEnable") action:@"enable"]];
NSArray *muteIntervals = @[
@(1 * 60 * 60),
@(8 * 60 * 60),
@(2 * 24 * 60 * 60),
];
for (NSNumber *nMuteInterval in muteIntervals)
{
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:[TGStringUtils stringForMuteInterval:[nMuteInterval intValue]] action:[[NSString alloc] initWithFormat:@"%@", nMuteInterval]]];
}
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"UserInfo.NotificationsDisable") action:@"disable"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
[[[TGActionSheet alloc] initWithTitle:nil actions:actions actionBlock:^(TGGroupInfoController *controller, NSString *action)
{
if ([action isEqualToString:@"enable"])
[controller _commitEnableNotifications:true orMuteFor:0];
else if ([action isEqualToString:@"disable"])
[controller _commitEnableNotifications:false orMuteFor:0];
else if (![action isEqualToString:@"cancel"])
{
[controller _commitEnableNotifications:false orMuteFor:[action intValue]];
}
} target:self] showInView:self.view];
}
- (void)_commitEnableNotifications:(bool)enable orMuteFor:(int)muteFor
{
int muteUntil = 0;
if (muteFor == 0)
{
if (enable)
muteUntil = 0;
else
muteUntil = INT_MAX;
}
else
{
muteUntil = (int)([[TGTelegramNetworking instance] approximateRemoteTime] + muteFor);
}
if (muteUntil != [_groupNotificationSettings[@"muteUntil"] intValue])
{
_groupNotificationSettings[@"muteUntil"] = @(muteUntil);
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(userInfoControllerMute%d)", _conversationId, actionId++] options:@{
@"peerId": @(_conversationId),
@"muteUntil": @(muteUntil)
} watcher:TGTelegraphInstance];
[self _updateNotificationItems:false];
}
}
- (void)addParticipantPressed
{
if ([self showGroupUpgradeNotice]) {
int64_t conversationId = _conversationId;
__weak TGGroupInfoController *weakSelf = self;
[[[TGAlertView alloc] initWithTitle:nil message:TGLocalized(@"Group.UpgradeConfirmation") cancelButtonTitle:TGLocalized(@"Common.Cancel") okButtonTitle:TGLocalized(@"Common.OK") completionBlock:^(bool okButtonPressed) {
if (okButtonPressed) {
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf->_ignoreUpdates = true;
}
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow show:true];
[[[[TGGroupManagementSignals migrateGroup:conversationId] deliverOn:[SQueue mainQueue]] onDispose:^{
TGDispatchOnMainThread(^{
[progressWindow dismiss:true];
});
}] startWithNext:^(TGConversation *conversation) {
[[TGInterfaceManager instance] navigateToConversationWithId:conversation.conversationId conversation:conversation];
} error:^(__unused id error) {
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf->_ignoreUpdates = false;
}
} completed:nil];
}
}] show];
} else {
int contactsMode = TGContactsModeRegistered | TGContactsModeManualFirstSection;
if (_conversation.chatParticipants.chatAdminId == TGTelegraphInstance.clientUserId)
contactsMode |= TGContactsModeCreateGroupLink;
contactsMode |= TGContactsModeIgnorePrivateBots;
TGGroupInfoSelectContactController *selectContactController = [[TGGroupInfoSelectContactController alloc] initWithContactsMode:contactsMode];
selectContactController.delegate = self;
NSMutableArray *disabledUsers = [[NSMutableArray alloc] init];
[disabledUsers addObjectsFromArray:_conversation.chatParticipants.chatParticipantUids];
[disabledUsers addObjectsFromArray:_soonToBeAddedUserIds];
selectContactController.disabledUsers = disabledUsers;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[selectContactController] navigationBarClass:[TGWhiteNavigationBar class]];
if ([self inPopover])
{
navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
navigationController.presentationStyle = TGNavigationControllerPresentationStyleChildInPopover;
}
[self presentViewController:navigationController animated:true completion:nil];
}
}
- (void)selectContactControllerDidSelectUser:(TGUser *)user
{
if (user.uid != 0 && ![_conversation.chatParticipants.chatParticipantUids containsObject:@(user.uid)])
{
__weak typeof(self) weakSelf = self;
[[[TGAlertView alloc] initWithTitle:nil message:[[NSString alloc] initWithFormat:TGLocalized(@"GroupInfo.AddParticipantConfirmation"), user.displayFirstName] cancelButtonTitle:TGLocalized(@"Common.Cancel") okButtonTitle:TGLocalized(@"Common.OK") completionBlock:^(bool okButtonPressed)
{
if (okButtonPressed)
{
TGGroupInfoController *strongSelf = weakSelf;
[strongSelf _commitAddParticipant:user];
}
}] show];
}
}
- (void)selectContactControllerDidSelectCreateLink
{
if ([self.presentedViewController isKindOfClass:[UINavigationController class]])
{
TGGroupInfoShareLinkController *controller = [[TGGroupInfoShareLinkController alloc] initWithPeerId:_conversationId accessHash:0 currentLink:_conversation.chatParticipants.exportedChatInviteString];
[(UINavigationController *)self.presentedViewController pushViewController:controller animated:true];
}
}
- (void)_commitAddParticipant:(TGUser *)user
{
if (![_soonToBeAddedUserIds containsObject:@(user.uid)])
{
[_soonToBeAddedUserIds addObject:@(user.uid)];
[self _loadUsersAndUpdateConversation:_conversation];
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]])
{
if (((TGGroupInfoUserCollectionItem *)item).user.uid == user.uid)
{
NSIndexPath *indexPath = [self indexPathForItem:item];
if (indexPath != nil && [UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad)
[self.collectionView selectItemAtIndexPath:indexPath animated:false scrollPosition:UICollectionViewScrollPositionTop];
break;
}
}
}
NSString *path = [NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/addMember/(%d)", _conversation.conversationId, user.uid];
NSDictionary *options = @{@"conversationId": @(_conversationId), @"uid": @(user.uid)};
[ActionStageInstance() dispatchOnStageQueue:^
{
[ActionStageInstance() requestActor:path options:options watcher:self];
[ActionStageInstance() requestActor:path options:options watcher:TGTelegraphInstance];
}];
}
[self dismissViewControllerAnimated:true completion:nil];
}
- (void)_commitDeleteParticipant:(int32_t)uid
{
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]] && ((TGGroupInfoUserCollectionItem *)item).user.uid == uid)
{
[_soonToBeRemovedUserIds addObject:@(uid)];
[(TGGroupInfoUserCollectionItem *)item setDisabled:true];
[(TGGroupInfoUserCollectionItem *)item setCanEdit:false animated:true];
NSDictionary *options = @{@"conversationId": @(_conversationId), @"uid": @(uid)};
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/deleteMember/(%d)", _conversationId, uid] options:options watcher:self];
break;
}
}
}
- (void)_commitUpdateTitle:(NSString *)title
{
[_groupInfoItem setUpdatingTitle:title];
[ActionStageInstance() dispatchOnStageQueue:^
{
static int actionId = 0;
NSString *path = [[NSString alloc] initWithFormat:@"/tg/conversation/(%" PRId64 ")/changeTitle/(groupInfoController%d)", _conversation.conversationId, actionId++];
NSDictionary *options = @{@"conversationId": @(_conversationId), @"title": title == nil ? @"" : title};
[ActionStageInstance() requestActor:path options:options watcher:self];
[ActionStageInstance() requestActor:path options:options watcher:TGTelegraphInstance];
}];
}
- (void)leaveGroupPressed
{
__weak typeof(self) weakSelf = self;
[[[TGActionSheet alloc] initWithTitle:TGLocalized(@"GroupInfo.DeleteAndExitConfirmation") actions:@[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"GroupInfo.DeleteAndExit") action:@"leave" type:TGActionSheetActionTypeDestructive],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]
] actionBlock:^(__unused id target, NSString *action)
{
if ([action isEqualToString:@"leave"])
{
TGGroupInfoController *strongSelf = weakSelf;
[strongSelf _commitLeaveGroup];
}
} target:self] showInView:self.view];
}
- (void)_commitLeaveGroup
{
[TGAppDelegateInstance.rootController.dialogListController.dialogListCompanion deleteItem:[[TGConversation alloc] initWithConversationId:_conversationId unreadCount:0 serviceUnreadCount:0] animated:false];
if (self.popoverController != nil)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self.popoverController dismissPopoverAnimated:true];
});
}
else
[self.navigationController popToRootViewControllerAnimated:true];
}
- (void)_changeNotificationSettings:(bool)enableNotifications
{
_groupNotificationSettings[@"muteUntil"] = @(!enableNotifications ? INT_MAX : 0);
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(groupInfoController%d)", _conversation.conversationId, actionId++] options:@{@"peerId": @(_conversationId), @"muteUntil": @(!enableNotifications ? INT_MAX : 0)} watcher:TGTelegraphInstance];
}
- (void)soundPressed
{
TGAlertSoundController *alertSoundController = [[TGAlertSoundController alloc] initWithTitle:TGLocalized(@"GroupInfo.Sound") soundInfoList:[self _soundInfoListForSelectedSoundId:[_groupNotificationSettings[@"soundId"] intValue]]];
alertSoundController.delegate = self;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[alertSoundController] navigationBarClass:[TGWhiteNavigationBar class]];
if ([self inPopover])
{
navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
navigationController.presentationStyle = TGNavigationControllerPresentationStyleChildInPopover;
}
[self presentViewController:navigationController animated:true completion:nil];
}
- (void)alertSoundController:(TGAlertSoundController *)__unused alertSoundController didFinishPickingWithSoundInfo:(NSDictionary *)soundInfo
{
if (soundInfo[@"soundId"] != nil && [soundInfo[@"soundId"] intValue] >= 0 && [soundInfo[@"soundId"] intValue] != [_groupNotificationSettings[@"soundId"] intValue])
{
int soundId = [soundInfo[@"soundId"] intValue];
_groupNotificationSettings[@"soundId"] = @(soundId);
[self _updateNotificationItems:false];
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(groupInfoController%d)", _conversationId, actionId++] options:@{
@"peerId": @(_conversationId),
@"soundId": @(soundId)
} watcher:TGTelegraphInstance];
}
}
- (NSString *)soundNameFromId:(int)soundId
{
if (soundId == 0 || soundId == 1)
return [TGAppDelegateInstance modernAlertSoundTitles][soundId];
if (soundId >= 2 && soundId <= 9)
return [TGAppDelegateInstance classicAlertSoundTitles][MAX(0, soundId - 2)];
if (soundId >= 100 && soundId <= 111)
return [TGAppDelegateInstance modernAlertSoundTitles][soundId - 100 + 2];
return @"";
}
- (NSArray *)_soundInfoListForSelectedSoundId:(int)selectedSoundId
{
NSMutableArray *infoList = [[NSMutableArray alloc] init];
int defaultSoundId = 1;
[TGDatabaseInstance() loadPeerNotificationSettings:INT_MAX - 2 soundId:&defaultSoundId muteUntil:NULL previewText:NULL messagesMuted:NULL notFound:NULL];
NSString *defaultSoundTitle = [self soundNameFromId:defaultSoundId];
int index = -1;
for (NSString *soundName in [TGAppDelegateInstance modernAlertSoundTitles])
{
index++;
int soundId = 0;
bool isDefault = false;
if (index == 1)
{
soundId = 1;
isDefault = true;
}
else if (index == 0)
soundId = 0;
else
soundId = index + 100 - 2;
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"title"] = isDefault ? [[NSString alloc] initWithFormat:@"%@ (%@)", soundName, defaultSoundTitle] : soundName;
dict[@"selected"] = @(selectedSoundId == soundId);
dict[@"soundName"] = [[NSString alloc] initWithFormat:@"%d", isDefault ? defaultSoundId : soundId];
dict[@"soundId"] = @(soundId);
dict[@"groupId"] = @(0);
[infoList addObject:dict];
}
index = -1;
for (NSString *soundName in [TGAppDelegateInstance classicAlertSoundTitles])
{
index++;
int soundId = index + 2;
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"title"] = soundName;
dict[@"selected"] = @(selectedSoundId == soundId);
dict[@"soundName"] = [[NSString alloc] initWithFormat:@"%d", soundId];
dict[@"soundId"] = @(soundId);
dict[@"groupId"] = @(1);
[infoList addObject:dict];
}
return infoList;
}
- (void)sharedMediaPressed
{
[self.navigationController pushViewController:[[TGSharedMediaController alloc] initWithPeerId:_conversationId accessHash:0 important:true] animated:true];
//[[TGInterfaceManager instance] navigateToMediaListOfConversation:_conversationId navigationController:self.navigationController];
}
#pragma mark -
- (void)_updateNotificationItems:(bool)__unused animated
{
[_muteExpirationTimer invalidate];
_muteExpirationTimer = nil;
bool enabled = false;
int muteUntil = [_groupNotificationSettings[@"muteUntil"] intValue];
if (muteUntil <= [[TGTelegramNetworking instance] approximateRemoteTime]) {
enabled = true;
} else {
int muteExpiration = muteUntil - (int)[[TGTelegramNetworking instance] approximateRemoteTime];
if (muteExpiration >= 7 * 24 * 60 * 60) {
} else {
_muteExpirationTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(updateMuteExpiration) interval:2.0 repeat:true];
}
}
_notificationsItem.isOn = enabled;
int groupSoundId = [[_groupNotificationSettings objectForKey:@"soundId"] intValue];
_soundItem.variant = [self soundNameFromId:groupSoundId];
}
- (void)updateMuteExpiration
{
bool enabled = false;
int muteUntil = [_groupNotificationSettings[@"muteUntil"] intValue];
if (muteUntil <= [[TGTelegramNetworking instance] approximateRemoteTime]) {
enabled = true;
}
if (_notificationsItem.isOn != enabled) {
_notificationsItem.isOn = enabled;
}
}
- (void)_updateSharedMediaCount
{
//_sharedMediaItem.variant = _sharedMediaCount == 0 ? TGLocalized(@"GroupInfo.SharedMediaNone") : ( TGIsLocaleArabic() ? [TGStringUtils stringWithLocalizedNumber:_sharedMediaCount] : [[NSString alloc] initWithFormat:@"%d", _sharedMediaCount]);
_sharedMediaItem.variant = @"";
}
- (void)_loadUsersAndUpdateConversation:(TGConversation *)conversation
{
NSMutableArray *participantUsers = [[NSMutableArray alloc] init];
for (NSNumber *nUid in conversation.chatParticipants.chatParticipantUids)
{
TGUser *user = [TGDatabaseInstance() loadUser:[nUid int32Value]];
if (user != nil)
[participantUsers addObject:user];
}
TGDispatchOnMainThread(^
{
if (_ignoreUpdates) {
return;
}
for (NSNumber *nUid in _soonToBeAddedUserIds)
{
if (![conversation.chatParticipants.chatParticipantUids containsObject:nUid])
{
TGUser *user = [TGDatabaseInstance() loadUser:[nUid int32Value]];
if (user != nil)
[participantUsers addObject:user];
}
}
_conversation = conversation;
[_groupInfoItem setConversation:_conversation];
bool forceReload = false;
[self _updateLeftState];
[self _updateConversationWithLoadedUsers:participantUsers forceReload:forceReload];
[self resetSections];
});
}
- (bool)showGroupUpgradeNotice {
if (_conversation.isCreator) {
#ifdef INTERNAL_RELEASE
//return _conversation.chatParticipantCount >= 2;
#endif
if (_upgradeNoticeMemberLimit != 0) {
return _conversation.chatParticipantCount >= _upgradeNoticeMemberLimit;
}
}
return false;
}
- (void)_updateConversationWithLoadedUsers:(NSArray *)participantUsers forceReload:(bool)forceReload
{
NSDictionary *invitedDates = _conversation.chatParticipants.chatInvitedDates;
int32_t selfUid = TGTelegraphInstance.clientUserId;
NSArray *sortedUsers = [participantUsers 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;
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;
if (user1.presence.online)
{
NSNumber *nDate1 = invitedDates[[[NSNumber alloc] initWithInt:user1.uid]];
NSNumber *nDate2 = invitedDates[[[NSNumber alloc] initWithInt:user2.uid]];
if (nDate1 != nil && nDate2 != nil)
return [nDate1 intValue] < [nDate2 intValue] ? NSOrderedAscending : NSOrderedDescending;
else if (nDate1 != nil)
return NSOrderedAscending;
else if (nDate2 != nil)
return NSOrderedDescending;
else
return user1.uid < user2.uid ? NSOrderedAscending : NSOrderedDescending;
}
if (user1.presence.lastSeen < 0)
{
NSNumber *nDate1 = invitedDates[[[NSNumber alloc] initWithInt:user1.uid]];
NSNumber *nDate2 = invitedDates[[[NSNumber alloc] initWithInt:user2.uid]];
if (nDate1 != nil && nDate2 != nil)
return [nDate1 intValue] < [nDate2 intValue] ? NSOrderedAscending : NSOrderedDescending;
else
return user1.uid < user2.uid ? NSOrderedAscending : NSOrderedDescending;
}
return user1.presence.lastSeen > user2.presence.lastSeen ? NSOrderedAscending : NSOrderedDescending;
}];
if ([self showGroupUpgradeNotice]) {
[_usersSectionHeader setTitle:TGLocalized(@"Group.UpgradeNoticeHeader")];
if ([_usersSection indexOfItem:_usersSectionUpgradeNotice1] == NSNotFound) {
[_usersSection insertItem:_usersSectionUpgradeNotice1 atIndex:1];
[_usersSection insertItem:_usersSectionUpgradeNotice2 atIndex:2];
forceReload = true;
}
_addParticipantItem.title = TGLocalized(@"GroupInfo.UpgradeButton");
_addParticipantItem.icon = [UIImage imageNamed:@"GroupInfoIconUpgrade"];
} else {
_addParticipantItem.title = TGLocalized(@"GroupInfo.AddParticipant");
_addParticipantItem.icon = [UIImage imageNamed:@"GroupInfoIconAddMember.png"];
if ([_usersSection indexOfItem:_usersSectionUpgradeNotice1] != NSNotFound) {
[_usersSection deleteItem:_usersSectionUpgradeNotice1];
[_usersSection deleteItem:_usersSectionUpgradeNotice2];
forceReload = true;
}
NSString *title = @"";
if (sortedUsers.count == 1)
title = TGLocalized(@"GroupInfo.ParticipantCount_1");
else if (sortedUsers.count == 2)
title = TGLocalized(@"GroupInfo.ParticipantCount_2");
else if (sortedUsers.count >= 3 && sortedUsers.count <= 10)
title = [NSString localizedStringWithFormat:TGLocalized(@"GroupInfo.ParticipantCount_3_10"), [TGStringUtils stringWithLocalizedNumber:sortedUsers.count]];
else
title = [NSString localizedStringWithFormat:TGLocalized(@"GroupInfo.ParticipantCount_any"), [TGStringUtils stringWithLocalizedNumber:sortedUsers.count]];
[_usersSectionHeader setTitle:title];
}
NSUInteger sectionIndex = [self indexForSection:_usersSection];
if (sectionIndex != NSNotFound)
{
bool haveChanges = false;
NSInteger headButtonCount = 0;
NSInteger tailButtonCount = 0;
for (NSInteger i = 0; i < (NSInteger)_usersSection.items.count; i++) {
if ([_usersSection.items[i] isKindOfClass:[TGGroupInfoUserCollectionItem class]]) {
break;
} else {
headButtonCount++;
}
}
if (_usersSection.items.count - headButtonCount - tailButtonCount != sortedUsers.count)
haveChanges = true;
else
{
for (int i = (int)headButtonCount, j = 0; i < (int)_usersSection.items.count - headButtonCount - tailButtonCount; i++, j++)
{
TGGroupInfoUserCollectionItem *userItem = _usersSection.items[i];
TGUser *user = sortedUsers[j];
if (user.uid != userItem.user.uid)
{
haveChanges = true;
break;
}
}
}
if (haveChanges || forceReload)
{
int count = (int)(_usersSection.items.count - headButtonCount - tailButtonCount);
while (count > 0)
{
[self.menuSections deleteItemFromSection:sectionIndex atIndex:headButtonCount];
count--;
}
NSInteger insertIndex = headButtonCount;
for (TGUser *user in sortedUsers)
{
TGGroupInfoUserCollectionItem *userItem = [[TGGroupInfoUserCollectionItem alloc] init];
userItem.interfaceHandle = _actionHandle;
bool disabled = ![_conversation.chatParticipants.chatParticipantUids containsObject:@(user.uid)] || [_soonToBeRemovedUserIds containsObject:@(user.uid)];
userItem.selectable = user.uid != selfUid && !disabled;
bool canEditInPrinciple = false;
if (user.uid != selfUid) {
canEditInPrinciple = ((_conversation.chatParticipants.chatAdminId == selfUid || [_conversation.chatParticipants.chatInvitedBy[@(user.uid)] int32Value] == selfUid));
if (!canEditInPrinciple) {
if (_conversation.isCreator) {
canEditInPrinciple = true;
} else if (_conversation.hasAdmins) {
canEditInPrinciple = _conversation.isAdmin && ![_conversation.chatParticipants.chatAdminUids containsObject:@(user.uid)] && _conversation.chatParticipants.chatAdminId != user.uid;
}
}
}
bool canEdit = userItem.selectable && canEditInPrinciple;
[userItem setCanEdit:canEdit];
[userItem setUser:user];
[userItem setDisabled:disabled];
[self.menuSections insertItem:userItem toSection:sectionIndex atIndex:insertIndex];
insertIndex++;
}
/*self.collectionLayout.withoutAnimation = true;
[UIView performWithoutAnimation:^
{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
}];
self.collectionLayout.withoutAnimation = false;*/
[self.collectionView reloadData];
[self _updateAllowCellEditing:false];
}
}
}
- (void)_updateAllowCellEditing:(bool)animated
{
bool anyCanEdit = false;
int32_t selfUid = TGTelegraphInstance.clientUserId;
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]])
{
TGUser *user = ((TGGroupInfoUserCollectionItem *)item).user;
bool canEditInPrinciple = false;
if (user.uid != selfUid) {
canEditInPrinciple = ((_conversation.chatParticipants.chatAdminId == selfUid || [_conversation.chatParticipants.chatInvitedBy[@(user.uid)] int32Value] == selfUid));
if (!canEditInPrinciple) {
if (_conversation.isCreator) {
canEditInPrinciple = true;
} else if (_conversation.hasAdmins) {
canEditInPrinciple = _conversation.isAdmin && ![_conversation.chatParticipants.chatAdminUids containsObject:@(user.uid)] && _conversation.chatParticipants.chatAdminId != user.uid;
}
}
}
anyCanEdit |= canEditInPrinciple;
}
}
_haveEditableUsers = anyCanEdit;
[self.collectionView setAllowEditingCells:anyCanEdit animated:animated];
}
- (void)_updateRelativeTimestamps
{
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]])
{
[(TGGroupInfoUserCollectionItem *)item updateTimestamp];
}
}
}
- (void)_updateUsers:(NSArray *)users
{
bool updatedAnyUser = false;
NSMutableDictionary *userIdToUser = [[NSMutableDictionary alloc] init];
for (TGUser *user in users)
{
userIdToUser[@(user.uid)] = user;
}
NSMutableArray *participantUsers = [[NSMutableArray alloc] init];
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]])
{
TGGroupInfoUserCollectionItem *userItem = item;
TGUser *user = userIdToUser[@(userItem.user.uid)];
if (user != nil)
{
updatedAnyUser = true;
[userItem setUser:user];
}
[participantUsers addObject:userItem.user];
}
}
if (updatedAnyUser)
{
[self _updateConversationWithLoadedUsers:participantUsers forceReload:false];
}
}
- (void)_layoutLeftLabel:(UIInterfaceOrientation)orientation
{
if (_leftLabel != nil)
{
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:orientation];
CGSize labelSize = [_leftLabel sizeThatFits:CGSizeMake(screenSize.width - 10, screenSize.height)];
_leftLabel.frame = CGRectMake(CGFloor((screenSize.width - labelSize.width) / 2.0f), CGFloor((screenSize.height - labelSize.height) / 2.0f), labelSize.width, labelSize.height);
}
}
- (void)_updateLeftState
{
if ((_conversation.kickedFromChat || _conversation.leftChat || _conversation.isDeactivated) != (_leftLabel != nil))
{
if (_conversation.kickedFromChat || _conversation.leftChat || _conversation.isDeactivated)
{
_leftLabel = [[UILabel alloc] init];
_leftLabel.backgroundColor = [UIColor clearColor];
_leftLabel.textColor = UIColorRGB(0x999999);
if (_conversation.isDeactivated) {
_leftLabel.text = TGLocalized(@"GroupInfo.DeactivatedStatus");
} else {
_leftLabel.text = _conversation.kickedFromChat ? TGLocalized(@"GroupInfo.KickedStatus") : TGLocalized(@"GroupInfo.LeftStatus");
}
_leftLabel.font = TGSystemFontOfSize(17.0f);
_leftLabel.lineBreakMode = NSLineBreakByWordWrapping;
_leftLabel.numberOfLines = 0;
[self.view addSubview:_leftLabel];
[self _layoutLeftLabel:self.interfaceOrientation];
[self setRightBarButtonItem:nil];
}
else
{
[_leftLabel removeFromSuperview];
_leftLabel = nil;
if (_editing)
{
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Done") style:UIBarButtonItemStyleDone target:self action:@selector(donePressed)] animated:false];
}
else
{
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Edit") style:UIBarButtonItemStylePlain target:self action:@selector(editPressed)] animated:false];
}
}
}
self.collectionView.hidden = _conversation.kickedFromChat || _conversation.leftChat || _conversation.isDeactivated;
}
#pragma mark -
- (void)actionStageActionRequested:(NSString *)action options:(id)options
{
if ([action isEqualToString:@"deleteUser"])
{
int32_t uid = [options[@"uid"] int32Value];
if (uid != 0)
[self _commitDeleteParticipant:uid];
}
else 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];
}
}
}
else if ([action isEqualToString:@"editedTitleChanged"])
{
NSString *title = options[@"title"];
if (_editing)
self.navigationItem.rightBarButtonItem.enabled = title.length != 0;
}
else if ([action isEqualToString:@"openAvatar"])
{
if (_conversation.chatPhotoSmall.length == 0)
{
if (_setGroupPhotoItem.enabled)
[self setGroupPhotoPressed];
}
else
{
TGRemoteImageView *avatarView = [_groupInfoItem avatarView];
if (avatarView != nil && avatarView.image != nil)
{
TGModernGalleryController *modernGallery = [[TGModernGalleryController alloc] init];
modernGallery.model = [[TGGroupAvatarGalleryModel alloc] initWithMessageId:0 legacyThumbnailUrl:_conversation.chatPhotoSmall legacyUrl:_conversation.chatPhotoBig imageSize:CGSizeMake(640.0f, 640.0f)];
__weak TGGroupInfoController *weakSelf = self;
modernGallery.itemFocused = ^(id<TGModernGalleryItem> item)
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
((UIView *)strongSelf->_groupInfoItem.avatarView).hidden = true;
}
}
};
modernGallery.beginTransitionIn = ^UIView *(id<TGModernGalleryItem> item, __unused TGModernGalleryItemView *itemView)
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
return strongSelf->_groupInfoItem.avatarView;
}
}
return nil;
};
modernGallery.beginTransitionOut = ^UIView *(id<TGModernGalleryItem> item, __unused TGModernGalleryItemView *itemView)
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
return strongSelf->_groupInfoItem.avatarView;
}
}
return nil;
};
modernGallery.completedTransitionOut = ^
{
__strong TGGroupInfoController *strongSelf = weakSelf;
if (strongSelf != nil)
{
((UIView *)strongSelf->_groupInfoItem.avatarView).hidden = false;
}
};
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithParentController:self contentController:modernGallery];
controllerWindow.hidden = false;
}
}
}
else if ([action isEqualToString:@"showUpdatingAvatarOptions"])
{
[[[TGActionSheet alloc] initWithTitle:nil actions:@[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"GroupInfo.SetGroupPhotoStop") action:@"stop" type:TGActionSheetActionTypeDestructive],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]
] actionBlock:^(TGGroupInfoController *controller, NSString *action)
{
if ([action isEqualToString:@"stop"])
[controller _commitCancelAvatarUpdate];
} target:self] showInView:self.view];
}
}
- (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)__unused arguments
{
if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId]])
{
TGConversation *conversation = ((SGraphObjectNode *)resource).object;
if (conversation != nil)
[self _loadUsersAndUpdateConversation:conversation];
}
else if ([path hasPrefix:@"/tg/peerSettings/"])
{
[self actorCompleted:ASStatusSuccess path:path result:resource];
}
else if ([path isEqualToString:@"/as/updateRelativeTimestamps"])
{
TGDispatchOnMainThread(^
{
[self _updateRelativeTimestamps];
});
}
else if ([path isEqualToString:@"/tg/userdatachanges"] || [path isEqualToString:@"/tg/userpresencechanges"])
{
NSArray *users = ((SGraphObjectNode *)resource).object;
TGDispatchOnMainThread(^
{
[self _updateUsers:users];
});
}
else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/sharedMediaCount/(%" PRIx64 ")", (int64_t)_conversationId]])
{
TGDispatchOnMainThread(^
{
_sharedMediaCount = [resource intValue];
[self _updateSharedMediaCount];
});
}
}
- (void)actorCompleted:(int)status path:(NSString *)path result:(id)result
{
if ([path hasPrefix:@"/tg/peerSettings/"])
{
if (status == ASStatusSuccess)
{
NSDictionary *notificationSettings = ((SGraphObjectNode *)result).object;
TGDispatchOnMainThread(^
{
_groupNotificationSettings = [notificationSettings mutableCopy];
[self _updateNotificationItems:false];
});
}
}
else if ([path hasPrefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/addMember/", _conversation.conversationId]])
{
TGDispatchOnMainThread(^
{
NSRange range = [path rangeOfString:@"/addMember/("];
int32_t uid = (int32_t)[[path substringFromIndex:(range.location + range.length)] intValue];
[_soonToBeAddedUserIds removeObject:@(uid)];
if (status == ASStatusSuccess)
{
TGConversation *updatedConversation = [_conversation copy];
updatedConversation.chatParticipants = [_conversation.chatParticipants copy];
int32_t currentDate = (int32_t)[[TGTelegramNetworking instance] approximateRemoteTime];
[updatedConversation.chatParticipants addParticipantWithId:uid invitedBy:TGTelegraphInstance.clientUserId date:currentDate];
_conversation = updatedConversation;
[self _updateLeftState];
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]] && ((TGGroupInfoUserCollectionItem *)item).user.uid == uid)
{
[(TGGroupInfoUserCollectionItem *)item setDisabled:false];
[(TGGroupInfoUserCollectionItem *)item setCanEdit:true animated:true];
break;
}
}
}
else
{
[self _loadUsersAndUpdateConversation:_conversation];
NSString *errorText = TGLocalized(@"ConversationProfile.UnknownAddMemberError");
if (status == -2)
{
TGUser *user = [TGDatabaseInstance() loadUser:uid];
if (user != nil)
errorText = [[NSString alloc] initWithFormat:TGLocalized(@"ConversationProfile.UserLeftChatError"), user.displayName];
}
else if (status == -3)
{
errorText = TGLocalized(@"ConversationProfile.UsersTooMuchError");
}
else if (status == -4)
errorText = TGLocalized(@"GroupInfo.AddUserLeftError");
else if (status == -5) {
NSString *format = TGLocalized(@"Privacy.GroupsAndChannels.InviteToGroupError");
TGUser *user = [TGDatabaseInstance() loadUser:uid];
errorText = [[NSString alloc] initWithFormat:format, user.displayFirstName, user.displayFirstName];
}
[[[TGAlertView alloc] initWithTitle:nil message:errorText cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
});
}
else if ([path hasPrefix:[NSString stringWithFormat:@"/tg/conversation/(%" PRId64 ")/deleteMember/", _conversationId]])
{
NSRange range = [path rangeOfString:@"/deleteMember/("];
int32_t uid = (int32_t)[[path substringFromIndex:(range.location + range.length)] intValue];
TGDispatchOnMainThread(^
{
[_soonToBeRemovedUserIds removeObject:@(uid)];
if (status == ASStatusSuccess)
{
TGConversation *updatedConversation = [_conversation copy];
updatedConversation.chatParticipants = [_conversation.chatParticipants copy];
[updatedConversation.chatParticipants removeParticipantWithId:uid];
_conversation = updatedConversation;
[self _updateLeftState];
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]] && ((TGGroupInfoUserCollectionItem *)item).user.uid == uid)
{
NSIndexPath *indexPath = [self indexPathForItem:item];
if (indexPath != nil)
{
[self.menuSections beginRecordingChanges];
[self.menuSections deleteItemFromSection:indexPath.section atIndex:indexPath.item];
[self.menuSections commitRecordedChanges:self.collectionView];
[self _updateAllowCellEditing:true];
}
break;
}
}
}
else
{
for (id item in _usersSection.items)
{
if ([item isKindOfClass:[TGGroupInfoUserCollectionItem class]] && ((TGGroupInfoUserCollectionItem *)item).user.uid == uid)
{
[(TGGroupInfoUserCollectionItem *)item setDisabled:false];
break;
}
}
}
});
}
else if ([path hasPrefix:[[NSString alloc] initWithFormat:@"/tg/conversation/(%" PRId64 ")/changeTitle/", _conversation.conversationId]])
{
TGDispatchOnMainThread(^
{
[_groupInfoItem setUpdatingTitle:nil];
if (status == ASStatusSuccess)
{
TGConversation *resultConversation = ((SGraphObjectNode *)result).object;
TGConversation *updatedConversation = [_conversation copy];
updatedConversation.chatTitle = resultConversation.chatTitle;
_conversation = updatedConversation;
[self _updateLeftState];
[_groupInfoItem setConversation:_conversation];
}
});
}
else if ([path hasPrefix:[[NSString alloc] initWithFormat:@"/tg/conversation/(%" PRId64 ")/updateAvatar/", _conversation.conversationId]])
{
TGDispatchOnMainThread(^
{
if (status == ASStatusSuccess)
{
TGConversation *resultConversation = ((SGraphObjectNode *)result).object;
TGConversation *updatedConversation = [_conversation copy];
updatedConversation.chatPhotoSmall = resultConversation.chatPhotoSmall;
updatedConversation.chatPhotoMedium = resultConversation.chatPhotoMedium;
updatedConversation.chatPhotoBig = resultConversation.chatPhotoBig;
_conversation = updatedConversation;
[self _updateLeftState];
[_groupInfoItem copyUpdatingAvatarToCacheWithUri:_conversation.chatPhotoSmall];
[_groupInfoItem setConversation:_conversation];
[_groupInfoItem setUpdatingAvatar:nil hasUpdatingAvatar:false];
[_setGroupPhotoItem setEnabled:true];
}
else
{
[_groupInfoItem setUpdatingAvatar:nil hasUpdatingAvatar:false];
[_setGroupPhotoItem setEnabled:true];
}
});
}
}
- (void)chatAdminsPressed {
TGGroupAdminsController *controller = [[TGGroupAdminsController alloc] initWithPeerId:_conversationId];
[self.navigationController pushViewController:controller animated:true];
}
@end