mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
495 lines
20 KiB
Objective-C
495 lines
20 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 "TGCreateGroupController.h"
|
|
|
|
#import "ActionStage.h"
|
|
#import "SGraphObjectNode.h"
|
|
|
|
#import "TGDatabase.h"
|
|
|
|
#import "TGInterfaceManager.h"
|
|
#import "TGAppDelegate.h"
|
|
|
|
#import "TGGroupInfoCollectionItem.h"
|
|
#import "TGGroupInfoUserCollectionItem.h"
|
|
#import "TGHeaderCollectionItem.h"
|
|
#import "TGCommentCollectionItem.h"
|
|
|
|
#import "TGProgressWindow.h"
|
|
|
|
#import "TGAlertView.h"
|
|
|
|
#import "TGChannelManagementSignals.h"
|
|
#import "TGButtonCollectionItem.h"
|
|
#import "TGVariantCollectionItem.h"
|
|
|
|
#import "TGGroupInfoSelectContactController.h"
|
|
#import "TGNavigationBar.h"
|
|
#import "TGNavigationController.h"
|
|
|
|
#import "TGChannelAboutSetupController.h"
|
|
#import "TGChannelLinkSetupController.h"
|
|
|
|
#import "TGCollectionMultilineInputItem.h"
|
|
|
|
#import "TGSelectContactController.h"
|
|
|
|
#import "TGSetupChannelAfterCreationController.h"
|
|
|
|
#import "UIDevice+PlatformInfo.h"
|
|
#import "TGImageUtils.h"
|
|
#import "TGActionSheet.h"
|
|
|
|
#import "TGRemoteImageView.h"
|
|
|
|
#import "TGUploadFileSignals.h"
|
|
|
|
#import "TGChannelIntroController.h"
|
|
|
|
#import "TGGroupManagementSignals.h"
|
|
|
|
#import "TGMediaAvatarMenuMixin.h"
|
|
|
|
#import "TGTelegramNetworking.h"
|
|
|
|
@interface TGCreateGroupController () <TGGroupInfoSelectContactControllerDelegate, ASWatcher>
|
|
{
|
|
NSArray *_userIds;
|
|
bool _createChannel;
|
|
bool _createChannelGroup;
|
|
|
|
TGGroupInfoCollectionItem *_groupInfoItem;
|
|
TGButtonCollectionItem *_setGroupPhotoItem;
|
|
|
|
TGCollectionMenuSection *_usersSection;
|
|
|
|
TGCollectionMenuSection *_infoSection;
|
|
TGCollectionMultilineInputItem *_aboutInputItem;
|
|
|
|
bool _makeFieldFirstResponder;
|
|
|
|
TGButtonCollectionItem *_addParticipantItem;
|
|
|
|
SVariable *_uploadedPhotoFile;
|
|
SVariable *_canCreatePublic;
|
|
|
|
TGMediaAvatarMenuMixin *_avatarMixin;
|
|
}
|
|
|
|
@property (nonatomic, strong) ASHandle *actionHandle;
|
|
|
|
@end
|
|
|
|
@implementation TGCreateGroupController
|
|
|
|
- (instancetype)init
|
|
{
|
|
return [self initWithCreateChannel:false createChannelGroup:false];
|
|
}
|
|
|
|
- (instancetype)initWithCreateChannel:(bool)createChannel createChannelGroup:(bool)createChannelGroup
|
|
{
|
|
self = [super init];
|
|
if (self)
|
|
{
|
|
_createChannel = createChannel;
|
|
_createChannelGroup = createChannelGroup;
|
|
|
|
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
|
|
|
|
NSString *titleString = TGLocalized(@"Compose.NewGroup");
|
|
if (_createChannel) {
|
|
titleString = TGLocalized(@"Compose.NewChannel");
|
|
}
|
|
|
|
[self setTitleText:titleString];
|
|
if (_createChannel) {
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
|
|
{
|
|
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Close") style:UIBarButtonItemStylePlain target:self action:@selector(closePressed)]];
|
|
}
|
|
|
|
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Next") style:UIBarButtonItemStyleDone target:self action:@selector(createPressed)]];
|
|
} else {
|
|
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Create") style:UIBarButtonItemStyleDone target:self action:@selector(createPressed)]];
|
|
}
|
|
self.navigationItem.rightBarButtonItem.enabled = false;
|
|
|
|
_groupInfoItem = [[TGGroupInfoCollectionItem alloc] init];
|
|
_groupInfoItem.isChannel = _createChannel;
|
|
_groupInfoItem.interfaceHandle = _actionHandle;
|
|
[_groupInfoItem setConversation:nil];
|
|
[_groupInfoItem setEditing:true];
|
|
|
|
_setGroupPhotoItem = [[TGButtonCollectionItem alloc] initWithTitle:_createChannel ? TGLocalized(@"Channel.UpdatePhotoItem") : TGLocalized(@"GroupInfo.SetGroupPhoto") action:@selector(setGroupPhotoPressed)];
|
|
_setGroupPhotoItem.titleColor = TGAccentColor();
|
|
_setGroupPhotoItem.deselectAutomatically = true;
|
|
|
|
TGCollectionMenuSection *groupInfoSection = [[TGCollectionMenuSection alloc] initWithItems:@[_groupInfoItem, _setGroupPhotoItem]];
|
|
[self.menuSections addSection:groupInfoSection];
|
|
|
|
_usersSection = [[TGCollectionMenuSection alloc] init];
|
|
|
|
if (_createChannel) {
|
|
_canCreatePublic = [[SVariable alloc] init];
|
|
[_canCreatePublic set:[TGChannelManagementSignals canMakePublicChannels]];
|
|
|
|
_aboutInputItem = [[TGCollectionMultilineInputItem alloc] init];
|
|
_aboutInputItem.maxLength = 200;
|
|
_aboutInputItem.placeholder = TGLocalized(@"Channel.About.Placeholder");
|
|
__weak TGCreateGroupController *weakSelf = self;
|
|
_aboutInputItem.heightChanged = ^ {
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
[strongSelf.collectionLayout invalidateLayout];
|
|
[strongSelf.collectionView layoutSubviews];
|
|
}
|
|
};
|
|
|
|
TGCommentCollectionItem *commentItem = [[TGCommentCollectionItem alloc] initWithFormattedText:TGLocalized(@"Channel.About.Help")];
|
|
commentItem.topInset = 1.0f;
|
|
|
|
_infoSection = [[TGCollectionMenuSection alloc] initWithItems:@[_aboutInputItem, commentItem]];
|
|
[self.menuSections addSection:_infoSection];
|
|
} else {
|
|
[self.menuSections addSection:_usersSection];
|
|
}
|
|
|
|
_makeFieldFirstResponder = true;
|
|
|
|
_uploadedPhotoFile = [[SVariable alloc] init];
|
|
[_uploadedPhotoFile set:[SSignal single:nil]];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_actionHandle reset];
|
|
[ActionStageInstance() removeWatcher:self];
|
|
}
|
|
|
|
- (void)closePressed {
|
|
[TGAppDelegateInstance.rootController clearContentControllers];
|
|
}
|
|
|
|
- (void)_resetCollectionView {
|
|
[super _resetCollectionView];
|
|
|
|
if (iosMajorVersion() >= 7) {
|
|
self.collectionView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
|
|
}
|
|
}
|
|
|
|
- (void)createPressed
|
|
{
|
|
if (_groupInfoItem.editingTitle.length != 0) {
|
|
if (_createChannel) {
|
|
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
[progressWindow show:true];
|
|
__weak TGCreateGroupController *weakSelf = self;
|
|
|
|
SSignal *createSignal = [TGChannelManagementSignals makeChannelWithTitle:_groupInfoItem.editingTitle about:_aboutInputItem.text group:false];
|
|
|
|
SSignal *createAndExportLink = [createSignal mapToSignal:^SSignal *(TGConversation *conversation) {
|
|
return [[TGChannelManagementSignals exportChannelInvitationLink:conversation.conversationId accessHash:conversation.accessHash] map:^id(NSString *link) {
|
|
return @{@"conversation": conversation, @"link": link};
|
|
}];
|
|
}];
|
|
|
|
SSignal *uploadedPhotoFileSignal = [[_uploadedPhotoFile signal] take:1];
|
|
|
|
SSignal *createAndUpdatePhoto = [createAndExportLink mapToSignal:^SSignal *(NSDictionary *dict) {
|
|
TGConversation *conversation = dict[@"conversation"];
|
|
|
|
return [uploadedPhotoFileSignal mapToSignal:^SSignal *(id inputFile) {
|
|
if (inputFile == nil) {
|
|
return [SSignal single:dict];
|
|
} else {
|
|
return [[[TGChannelManagementSignals updateChannelPhoto:conversation.conversationId accessHash:conversation.accessHash uploadedFile:[SSignal single:inputFile]] mapToSignal:^SSignal *(__unused id next) {
|
|
return [SSignal complete];
|
|
}] then:[SSignal single:dict]];
|
|
}
|
|
}];
|
|
}];
|
|
|
|
[[[createAndUpdatePhoto deliverOn:[SQueue mainQueue]] onDispose:^{
|
|
TGDispatchOnMainThread(^{
|
|
[progressWindow dismiss:true];
|
|
});
|
|
}] startWithNext:^(NSDictionary *dict) {
|
|
TGConversation *conversation = dict[@"conversation"];
|
|
NSString *link = dict[@"link"];
|
|
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
TGSetupChannelAfterCreationController *setupController = [[TGSetupChannelAfterCreationController alloc] initWithConversation:conversation exportedLink:link];
|
|
|
|
NSMutableArray *viewControllers = [[NSMutableArray alloc] initWithArray:strongSelf.navigationController.viewControllers];
|
|
if (viewControllers.count != 1) {
|
|
[viewControllers removeObjectsInRange:NSMakeRange(1, viewControllers.count - 1)];
|
|
}
|
|
[viewControllers addObject:setupController];
|
|
|
|
[strongSelf.navigationController setViewControllers:viewControllers animated:true];
|
|
}
|
|
} error:^(__unused id error) {
|
|
} completed:^{
|
|
}];
|
|
} else if ((_userIds.count != 0 || _createChannelGroup) && (_groupInfoItem.editingTitle.length != 0))
|
|
{
|
|
if (_createChannelGroup) {
|
|
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
[progressWindow show:true];
|
|
__weak TGCreateGroupController *weakSelf = self;
|
|
|
|
SSignal *createSignal = [TGChannelManagementSignals makeChannelWithTitle:_groupInfoItem.editingTitle about:_aboutInputItem.text group:true];
|
|
|
|
SSignal *createAndExportLink = [createSignal mapToSignal:^SSignal *(TGConversation *conversation) {
|
|
return [[TGChannelManagementSignals exportChannelInvitationLink:conversation.conversationId accessHash:conversation.accessHash] map:^id(NSString *link) {
|
|
return @{@"conversation": conversation, @"link": link};
|
|
}];
|
|
}];
|
|
|
|
SSignal *uploadedPhotoFileSignal = [[_uploadedPhotoFile signal] take:1];
|
|
|
|
SSignal *createAndUpdatePhoto = [createAndExportLink mapToSignal:^SSignal *(NSDictionary *dict) {
|
|
TGConversation *conversation = dict[@"conversation"];
|
|
|
|
return [uploadedPhotoFileSignal mapToSignal:^SSignal *(id inputFile) {
|
|
if (inputFile == nil) {
|
|
return [SSignal single:dict];
|
|
} else {
|
|
return [[[TGChannelManagementSignals updateChannelPhoto:conversation.conversationId accessHash:conversation.accessHash uploadedFile:[SSignal single:inputFile]] mapToSignal:^SSignal *(__unused id next) {
|
|
return [SSignal complete];
|
|
}] then:[SSignal single:dict]];
|
|
}
|
|
}];
|
|
}];
|
|
|
|
[[[createAndUpdatePhoto deliverOn:[SQueue mainQueue]] onDispose:^{
|
|
TGDispatchOnMainThread(^{
|
|
[progressWindow dismiss:true];
|
|
});
|
|
}] startWithNext:^(NSDictionary *dict) {
|
|
TGConversation *conversation = dict[@"conversation"];
|
|
NSString *link = dict[@"link"];
|
|
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
TGSetupChannelAfterCreationController *setupController = [[TGSetupChannelAfterCreationController alloc] initWithConversation:conversation exportedLink:link];
|
|
|
|
NSMutableArray *viewControllers = [[NSMutableArray alloc] initWithArray:strongSelf.navigationController.viewControllers];
|
|
if (viewControllers.count != 1) {
|
|
[viewControllers removeObjectsInRange:NSMakeRange(1, viewControllers.count - 1)];
|
|
}
|
|
[viewControllers addObject:setupController];
|
|
|
|
[strongSelf.navigationController setViewControllers:viewControllers animated:true];
|
|
}
|
|
} error:^(__unused id error) {
|
|
} completed:^{
|
|
}];
|
|
} else {
|
|
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
[progressWindow show:true];
|
|
__weak TGCreateGroupController *weakSelf = self;
|
|
|
|
NSMutableArray *users = [[NSMutableArray alloc] init];
|
|
for (NSNumber *nUserId in _userIds) {
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUserId intValue]];
|
|
if (user != nil) {
|
|
[users addObject:user];
|
|
}
|
|
}
|
|
|
|
SSignal *createSignal = [TGGroupManagementSignals makeGroupWithTitle:_groupInfoItem.editingTitle users:users];
|
|
|
|
SSignal *uploadedPhotoFileSignal = [[_uploadedPhotoFile signal] take:1];
|
|
|
|
SSignal *createAndUpdatePhoto = [createSignal mapToSignal:^SSignal *(TGConversation *conversation) {
|
|
return [uploadedPhotoFileSignal mapToSignal:^SSignal *(id inputFile) {
|
|
if (inputFile == nil) {
|
|
return [SSignal single:conversation];
|
|
} else {
|
|
return [[[TGGroupManagementSignals updateGroupPhoto:conversation.conversationId uploadedFile:[SSignal single:inputFile]] mapToSignal:^SSignal *(__unused id next) {
|
|
return [SSignal complete];
|
|
}] then:[SSignal single:conversation]];
|
|
}
|
|
}];
|
|
}];
|
|
|
|
[[[createAndUpdatePhoto deliverOn:[SQueue mainQueue]] onDispose:^{
|
|
TGDispatchOnMainThread(^{
|
|
[progressWindow dismiss:true];
|
|
});
|
|
}] startWithNext:^(TGConversation *conversation) {
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
[[TGInterfaceManager instance] navigateToConversationWithId:conversation.conversationId conversation:nil];
|
|
}
|
|
} error:^(id error) {
|
|
NSString *errorType = [[TGTelegramNetworking instance] extractNetworkErrorType:error];
|
|
NSString *errorText = TGLocalized(@"Profile.CreateEncryptedChatError");
|
|
if ([errorType isEqualToString:@"USERS_TOO_FEW"] || [errorType isEqualToString:@"USER_PRIVACY_RESTRICTED"]) {
|
|
errorText = TGLocalized(@"Privacy.GroupsAndChannels.InviteToChannelMultipleError");
|
|
}
|
|
|
|
[[[TGAlertView alloc] initWithTitle:nil message:errorText cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
|
|
} completed:^{
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
[super viewWillAppear:animated];
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
[super viewDidAppear:animated];
|
|
|
|
NSMutableArray *controllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
|
|
TGViewController *introController = nil;
|
|
for (UIViewController *controller in controllers)
|
|
{
|
|
if ([controller isKindOfClass:[TGChannelIntroController class]])
|
|
{
|
|
introController = (TGChannelIntroController *)controller;
|
|
break;
|
|
}
|
|
}
|
|
if (introController != nil)
|
|
{
|
|
[controllers removeObject:introController];
|
|
self.navigationController.viewControllers = controllers;
|
|
}
|
|
|
|
if (_makeFieldFirstResponder)
|
|
{
|
|
_makeFieldFirstResponder = false;
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[_groupInfoItem makeNameFieldFirstResponder];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)setUserIds:(NSArray *)userIds
|
|
{
|
|
_userIds = userIds;
|
|
|
|
NSMutableArray *users = [[NSMutableArray alloc] init];
|
|
for (NSNumber *nUid in _userIds)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid int32Value]];
|
|
if (user != nil)
|
|
[users addObject:user];
|
|
}
|
|
|
|
NSUInteger usersSectionIndex = [self indexForSection:_usersSection];
|
|
if (usersSectionIndex != NSNotFound)
|
|
{
|
|
for (int i = (int)_usersSection.items.count - 1; i >= 0; i--)
|
|
{
|
|
[self.menuSections deleteItemFromSection:usersSectionIndex atIndex:0];
|
|
}
|
|
}
|
|
|
|
for (TGUser *user in users)
|
|
{
|
|
TGGroupInfoUserCollectionItem *userItem = [[TGGroupInfoUserCollectionItem alloc] init];
|
|
[userItem setUser:user];
|
|
userItem.selectable = false;
|
|
[userItem setCanEdit:false];
|
|
[self.menuSections addItemToSection:usersSectionIndex item:userItem];
|
|
}
|
|
|
|
[self.collectionView reloadData];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)actionStageActionRequested:(NSString *)action options:(id)options
|
|
{
|
|
if ([action isEqualToString:@"editedTitleChanged"])
|
|
{
|
|
self.navigationItem.rightBarButtonItem.enabled = [_groupInfoItem.editingTitle stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length != 0;
|
|
TGConversation *conversation = [[TGConversation alloc] init];
|
|
conversation.chatTitle = _groupInfoItem.editingTitle;
|
|
[_groupInfoItem setConversation:conversation];
|
|
} else if ([action isEqualToString:@"openUser"]) {
|
|
[[TGInterfaceManager instance] navigateToProfileOfUser:[options[@"uid"] intValue]];
|
|
} else if ([action isEqualToString:@"openAvatar"]) {
|
|
[self setGroupPhotoPressed];
|
|
}
|
|
}
|
|
|
|
- (void)setGroupPhotoPressed
|
|
{
|
|
__weak TGCreateGroupController *weakSelf = self;
|
|
_avatarMixin = [[TGMediaAvatarMenuMixin alloc] initWithParentController:self hasDeleteButton:([_groupInfoItem staticAvatar] != nil)];
|
|
_avatarMixin.didFinishWithImage = ^(UIImage *image)
|
|
{
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf _updateGroupProfileImage:image];
|
|
strongSelf->_avatarMixin = nil;
|
|
};
|
|
_avatarMixin.didFinishWithDelete = ^
|
|
{
|
|
__strong TGCreateGroupController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf _commitDeleteAvatar];
|
|
strongSelf->_avatarMixin = nil;
|
|
};
|
|
_avatarMixin.didDismiss = ^
|
|
{
|
|
__strong TGCreateGroupController *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 setStaticAvatar:avatarImage];
|
|
[_uploadedPhotoFile set:[TGUploadFileSignals uploadedFileWithData:imageData]];
|
|
}
|
|
|
|
- (void)_commitDeleteAvatar
|
|
{
|
|
[_groupInfoItem setStaticAvatar:nil];
|
|
[_uploadedPhotoFile set:[SSignal single:nil]];
|
|
}
|
|
|
|
@end
|