2014-10-29 21:58:58 +01:00
//
// NewGroupViewController . m
// Signal
//
// Created by Dylan Bourgeois on 13 / 11 / 14.
// Copyright ( c ) 2014 Open Whisper Systems . All rights reserved .
//
2016-09-02 16:22:06 +02:00
# import "NewGroupViewController.h"
2014-11-21 14:38:37 +01:00
# import "Environment.h"
2014-12-24 02:25:10 +01:00
# import "FunctionalUtil.h"
2016-09-02 16:22:06 +02:00
# import "OWSContactsManager.h"
2014-12-17 06:44:36 +01:00
# import "SecurityUtils.h"
# import "SignalKeyingStorage.h"
2015-12-22 12:45:09 +01:00
# import "SignalsViewController.h"
2015-12-26 17:27:27 +01:00
# import "TSOutgoingMessage.h"
2014-12-31 19:31:27 +01:00
# import "UIImage+normalizeImage.h"
2014-11-24 21:51:43 +01:00
# import "UIUtil.h"
2016-09-02 16:22:06 +02:00
# import < MobileCoreServices / UTCoreTypes . h >
# import < SignalServiceKit / MimeTypeUtil . h >
# import < SignalServiceKit / NSDate + millisecondTimeStamp . h >
2016-10-14 22:59:58 +02:00
# import < SignalServiceKit / OWSMessageSender . h >
2016-09-02 16:22:06 +02:00
# import < SignalServiceKit / TSAccountManager . h >
2014-10-29 21:58:58 +01:00
2015-12-22 12:45:09 +01:00
static NSString * const kUnwindToMessagesViewSegue = @ "UnwindToMessagesViewSegue" ;
2014-12-24 02:25:10 +01:00
2014-10-29 21:58:58 +01:00
@ interface NewGroupViewController ( ) {
2015-12-22 12:45:09 +01:00
NSArray * contacts ;
2014-10-29 21:58:58 +01:00
}
2016-10-14 22:59:58 +02:00
2015-12-22 12:45:09 +01:00
@ property TSGroupThread * thread ;
2016-10-14 22:59:58 +02:00
@ property ( nonatomic , readonly ) OWSMessageSender * messageSender ;
2014-10-29 21:58:58 +01:00
@ end
2016-10-14 22:59:58 +02:00
2014-10-29 21:58:58 +01:00
@ implementation NewGroupViewController
2016-10-14 22:59:58 +02:00
- ( instancetype ) init
{
self = [ super init ] ;
if ( ! self ) {
return self ;
}
_messageSender = [ [ OWSMessageSender alloc ] initWithNetworkManager : [ Environment getCurrent ] . networkManager
storageManager : [ TSStorageManager sharedManager ]
contactsManager : [ Environment getCurrent ] . contactsManager
contactsUpdater : [ Environment getCurrent ] . contactsUpdater ] ;
return self ;
}
- ( instancetype ) initWithCoder : ( NSCoder * ) aDecoder
{
self = [ super initWithCoder : aDecoder ] ;
if ( ! self ) {
return self ;
}
_messageSender = [ [ OWSMessageSender alloc ] initWithNetworkManager : [ Environment getCurrent ] . networkManager
storageManager : [ TSStorageManager sharedManager ]
contactsManager : [ Environment getCurrent ] . contactsManager
contactsUpdater : [ Environment getCurrent ] . contactsUpdater ] ;
return self ;
}
2015-12-22 12:45:09 +01:00
- ( void ) configWithThread : ( TSGroupThread * ) gThread {
2014-12-17 06:44:36 +01:00
_thread = gThread ;
}
2014-10-29 21:58:58 +01:00
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
2015-01-14 22:30:01 +01:00
[ self . navigationController . navigationBar setTranslucent : NO ] ;
2016-11-18 18:42:03 +01:00
contacts = [ Environment getCurrent ] . contactsManager . signalContacts ;
2014-12-24 02:25:10 +01:00
2015-12-22 12:45:09 +01:00
2015-01-27 02:20:11 +01:00
self . tableView . tableHeaderView . frame = CGRectMake ( 0 , 0 , 400 , 44 ) ;
2015-12-22 12:45:09 +01:00
self . tableView . tableHeaderView = self . tableView . tableHeaderView ;
contacts = [ contacts filter : ^ int ( Contact * contact ) {
for ( PhoneNumber * number in [ contact parsedPhoneNumbers ] ) {
if ( [ [ number toE164 ] isEqualToString : [ TSAccountManager localNumber ] ] ) {
// remove local number
return NO ;
} else if ( _thread ! = nil && _thread . groupModel . groupMemberIds ) {
return ! [ _thread . groupModel . groupMemberIds containsObject : [ number toE164 ] ] ;
}
}
return YES ;
2014-12-24 02:25:10 +01:00
} ] ;
2015-12-22 12:45:09 +01:00
2014-12-24 02:25:10 +01:00
[ self initializeDelegates ] ;
[ self initializeTableView ] ;
[ self initializeKeyboardHandlers ] ;
2015-12-22 12:45:09 +01:00
if ( _thread = = nil ) {
self . navigationItem . rightBarButtonItem =
[ [ UIBarButtonItem alloc ] initWithImage : [ [ UIImage imageNamed : @ "add-conversation" ]
imageWithRenderingMode : UIImageRenderingModeAlwaysOriginal ]
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( createGroup ) ] ;
2015-02-19 07:00:05 +01:00
self . navigationItem . rightBarButtonItem . imageInsets = UIEdgeInsetsMake ( 0 , -10 , 0 , 10 ) ;
2015-12-22 12:45:09 +01:00
self . navigationItem . title = NSLocalizedString ( @ "NEW_GROUP_DEFAULT_TITLE" , @ "" ) ;
} else {
self . navigationItem . rightBarButtonItem =
[ [ UIBarButtonItem alloc ] initWithTitle : NSLocalizedString ( @ "UPDATE_BUTTON_TITLE" , @ "" )
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( updateGroup ) ] ;
self . navigationItem . title = _thread . groupModel . groupName ;
2014-12-17 06:44:36 +01:00
self . nameGroupTextField . text = _thread . groupModel . groupName ;
2015-12-22 12:45:09 +01:00
if ( _thread . groupModel . groupImage ! = nil ) {
2015-01-30 04:31:35 +01:00
_groupImage = _thread . groupModel . groupImage ;
2015-01-04 20:48:43 +01:00
[ self setupGroupImageButton : _thread . groupModel . groupImage ] ;
}
2014-12-17 06:44:36 +01:00
}
2015-02-18 23:21:03 +01:00
_nameGroupTextField . placeholder = NSLocalizedString ( @ "NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" , @ "" ) ;
2015-12-22 12:45:09 +01:00
_addPeopleLabel . text = NSLocalizedString ( @ "NEW_GROUP_REQUEST_ADDPEOPLE" , @ "" ) ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Initializers
2015-12-22 12:45:09 +01:00
- ( void ) initializeDelegates {
2014-10-29 21:58:58 +01:00
self . nameGroupTextField . delegate = self ;
}
2015-12-22 12:45:09 +01:00
- ( void ) initializeTableView {
self . tableView . tableFooterView = [ [ UIView alloc ] initWithFrame : CGRectZero ] ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Keyboard notifications
2015-12-22 12:45:09 +01:00
- ( void ) initializeKeyboardHandlers {
UITapGestureRecognizer * outsideTabRecognizer =
[ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( dismissKeyboardFromAppropriateSubView ) ] ;
2014-10-29 21:58:58 +01:00
[ self . tapToDismissView addGestureRecognizer : outsideTabRecognizer ] ;
}
2015-12-22 12:45:09 +01:00
- ( void ) dismissKeyboardFromAppropriateSubView {
2014-10-29 21:58:58 +01:00
[ self . nameGroupTextField resignFirstResponder ] ;
}
# pragma mark - Actions
2016-08-01 00:25:07 +02:00
- ( void ) createGroup
{
2015-12-22 12:45:09 +01:00
TSGroupModel * model = [ self makeGroup ] ;
2015-12-26 17:27:27 +01:00
[ [ TSStorageManager sharedManager ]
. dbConnection readWriteWithBlock : ^ ( YapDatabaseReadWriteTransaction * _Nonnull transaction ) {
self . thread = [ TSGroupThread getOrCreateThreadWithGroupModel : model transaction : transaction ] ;
} ] ;
2016-10-14 22:59:58 +02:00
void ( ^ popToThread ) ( ) = ^ {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self dismissViewControllerAnimated : YES
completion : ^ {
[ Environment messageGroup : self . thread ] ;
} ] ;
} ) ;
} ;
void ( ^ removeThreadWithError ) ( NSError * error ) = ^ ( NSError * error ) {
[ self . thread remove ] ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self dismissViewControllerAnimated : YES
completion : ^ {
SignalAlertView ( NSLocalizedString ( @ "GROUP_CREATING_FAILED" , nil ) ,
error . localizedDescription ) ;
} ] ;
} ) ;
} ;
2015-12-26 17:27:27 +01:00
UIAlertController * alertController =
[ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "GROUP_CREATING" , nil )
message : nil
preferredStyle : UIAlertControllerStyleAlert ] ;
2016-10-14 22:59:58 +02:00
[ self presentViewController : alertController
animated : YES
completion : ^ {
TSOutgoingMessage * message =
[ [ TSOutgoingMessage alloc ] initWithTimestamp : [ NSDate ows_millisecondTimeStamp ]
inThread : self . thread
messageBody : @ ""
attachmentIds : [ NSMutableArray new ] ] ;
message . groupMetaMessage = TSGroupMessageNew ;
message . customMessage = NSLocalizedString ( @ "GROUP_CREATED" , nil ) ;
if ( model . groupImage ) {
[ self . messageSender sendAttachmentData : UIImagePNGRepresentation ( model . groupImage )
contentType : OWSMimeTypeImagePng
inMessage : message
success : popToThread
failure : removeThreadWithError ] ;
} else {
[ self . messageSender sendMessage : message success : popToThread failure : removeThreadWithError ] ;
}
} ] ;
2014-12-17 06:44:36 +01:00
}
2016-08-01 00:25:07 +02:00
- ( void ) updateGroup
{
2015-12-22 12:45:09 +01:00
NSMutableArray * mut = [ [ NSMutableArray alloc ] init ] ;
for ( NSIndexPath * idx in _tableView . indexPathsForSelectedRows ) {
2015-01-27 02:20:11 +01:00
[ mut addObjectsFromArray : [ [ contacts objectAtIndex : ( NSUInteger ) idx . row ] textSecureIdentifiers ] ] ;
2014-12-24 02:25:10 +01:00
}
2015-01-30 04:31:35 +01:00
[ mut addObjectsFromArray : _thread . groupModel . groupMemberIds ] ;
2015-12-22 12:45:09 +01:00
2015-02-28 17:49:46 +01:00
_groupModel = [ [ TSGroupModel alloc ] initWithTitle : _nameGroupTextField . text
memberIds : [ [ [ NSSet setWithArray : mut ] allObjects ] mutableCopy ]
image : _thread . groupModel . groupImage
2016-08-01 00:25:07 +02:00
groupId : _thread . groupModel . groupId ] ;
2015-12-22 12:45:09 +01:00
2015-02-28 17:49:46 +01:00
[ self . nameGroupTextField resignFirstResponder ] ;
2015-12-22 12:45:09 +01:00
2014-12-24 02:25:10 +01:00
[ self performSegueWithIdentifier : kUnwindToMessagesViewSegue sender : self ] ;
2014-10-29 21:58:58 +01:00
}
2014-12-17 06:44:36 +01:00
2016-08-01 00:25:07 +02:00
- ( TSGroupModel * ) makeGroup
{
2015-12-22 12:45:09 +01:00
NSString * title = _nameGroupTextField . text ;
NSMutableArray * mut = [ [ NSMutableArray alloc ] init ] ;
for ( NSIndexPath * idx in _tableView . indexPathsForSelectedRows ) {
2015-01-27 02:20:11 +01:00
[ mut addObjectsFromArray : [ [ contacts objectAtIndex : ( NSUInteger ) idx . row ] textSecureIdentifiers ] ] ;
2014-10-29 21:58:58 +01:00
}
2015-12-22 12:45:09 +01:00
[ mut addObject : [ TSAccountManager localNumber ] ] ;
NSData * groupId = [ SecurityUtils generateRandomBytes : 16 ] ;
2016-08-01 00:25:07 +02:00
return [ [ TSGroupModel alloc ] initWithTitle : title memberIds : mut image : _groupImage groupId : groupId ] ;
2014-10-29 21:58:58 +01:00
}
2015-12-22 12:45:09 +01:00
- ( IBAction ) addGroupPhoto : ( id ) sender {
2016-11-26 00:12:00 +01:00
UIAlertController * actionSheetController = [ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "NEW_GROUP_ADD_PHOTO_ACTION" , @ "Action Sheet title prompting the user for a group avatar" )
message : nil
preferredStyle : UIAlertControllerStyleActionSheet ] ;
UIAlertAction * dismissAction = [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "TXT_CANCEL_TITLE" , @ "" )
style : UIAlertActionStyleCancel
handler : nil ] ;
[ actionSheetController addAction : dismissAction ] ;
UIAlertAction * takePictureAction = [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "TAKE_PICTURE_BUTTON" , @ "" )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * _Nonnull action ) {
[ self takePicture ] ;
} ] ;
[ actionSheetController addAction : takePictureAction ] ;
UIAlertAction * choosePictureAction = [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "CHOOSE_MEDIA_BUTTON" , @ "" )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * _Nonnull action ) {
[ self chooseFromLibrary ] ;
} ] ;
[ actionSheetController addAction : choosePictureAction ] ;
[ self presentViewController : actionSheetController animated : true completion : nil ] ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Group Image
2015-12-22 12:45:09 +01:00
- ( void ) takePicture {
2014-10-29 21:58:58 +01:00
UIImagePickerController * picker = [ [ UIImagePickerController alloc ] init ] ;
2015-12-22 12:45:09 +01:00
picker . delegate = self ;
picker . allowsEditing = NO ;
picker . sourceType = UIImagePickerControllerSourceTypeCamera ;
if ( [ UIImagePickerController isSourceTypeAvailable : UIImagePickerControllerSourceTypeCamera ] ) {
picker . mediaTypes = [ [ NSArray alloc ] initWithObjects : ( NSString * ) kUTTypeImage , nil ] ;
2015-01-30 09:49:40 +01:00
[ self presentViewController : picker animated : YES completion : [ UIUtil modalCompletionBlock ] ] ;
2014-10-29 21:58:58 +01:00
}
}
2015-12-22 12:45:09 +01:00
- ( void ) chooseFromLibrary {
2014-10-29 21:58:58 +01:00
UIImagePickerController * picker = [ [ UIImagePickerController alloc ] init ] ;
2015-12-22 12:45:09 +01:00
picker . delegate = self ;
picker . sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum ;
if ( [ UIImagePickerController isSourceTypeAvailable : UIImagePickerControllerSourceTypeSavedPhotosAlbum ] ) {
2014-10-29 21:58:58 +01:00
picker . mediaTypes = [ [ NSArray alloc ] initWithObjects : ( NSString * ) kUTTypeImage , nil ] ;
2015-01-30 09:49:40 +01:00
[ self presentViewController : picker animated : YES completion : [ UIUtil modalCompletionBlock ] ] ;
2014-10-29 21:58:58 +01:00
}
}
/ *
* Dismissing UIImagePickerController
* /
- ( void ) imagePickerControllerDidCancel : ( UIImagePickerController * ) picker {
[ self dismissViewControllerAnimated : YES completion : nil ] ;
}
/ *
* Fetch data from UIImagePickerController
* /
2015-12-22 12:45:09 +01:00
- ( void ) imagePickerController : ( UIImagePickerController * ) picker didFinishPickingMediaWithInfo : ( NSDictionary * ) info {
2014-10-29 21:58:58 +01:00
UIImage * picture_camera = [ info objectForKey : UIImagePickerControllerOriginalImage ] ;
2015-12-22 12:45:09 +01:00
2014-10-29 21:58:58 +01:00
if ( picture_camera ) {
2015-12-22 12:45:09 +01:00
UIImage * small = [ picture_camera resizedImageToFitInSize : CGSizeMake ( 100.00 , 100.00 ) scaleIfSmaller : NO ] ;
2015-01-04 20:48:43 +01:00
_thread . groupModel . groupImage = small ;
2015-12-22 12:45:09 +01:00
_groupImage = small ;
2015-01-04 20:48:43 +01:00
[ self setupGroupImageButton : small ] ;
2014-10-29 21:58:58 +01:00
}
[ self dismissViewControllerAnimated : YES completion : nil ] ;
}
2015-12-22 12:45:09 +01:00
- ( void ) setupGroupImageButton : ( UIImage * ) image {
2015-01-30 04:31:35 +01:00
[ _groupImageButton setImage : image forState : UIControlStateNormal ] ;
2015-12-22 12:45:09 +01:00
_groupImageButton . imageView . layer . cornerRadius = CGRectGetWidth ( [ _groupImageButton . imageView frame ] ) / 2.0 f ;
2015-01-30 04:31:35 +01:00
_groupImageButton . imageView . layer . masksToBounds = YES ;
2015-12-22 12:45:09 +01:00
_groupImageButton . imageView . layer . borderColor = [ [ UIColor lightGrayColor ] CGColor ] ;
_groupImageButton . imageView . layer . borderWidth = 0.5 f ;
2016-04-22 21:34:09 +02:00
_groupImageButton . imageView . contentMode = UIViewContentModeScaleAspectFill ;
2014-12-24 02:25:10 +01:00
}
2014-10-29 21:58:58 +01:00
# pragma mark - Table view data source
- ( NSInteger ) numberOfSectionsInTableView : ( UITableView * ) tableView {
return 1 ;
}
- ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
2015-01-27 02:20:11 +01:00
return ( NSInteger ) [ contacts count ] ;
2014-10-29 21:58:58 +01:00
}
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath {
UITableViewCell * cell = [ tableView dequeueReusableCellWithIdentifier : @ "SearchCell" ] ;
2015-12-22 12:45:09 +01:00
2014-10-29 21:58:58 +01:00
if ( cell = = nil ) {
2015-01-27 02:20:11 +01:00
cell = [ [ UITableViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier : @ "GroupSearchCell" ] ;
2014-10-29 21:58:58 +01:00
}
2015-12-22 12:45:09 +01:00
NSUInteger row = ( NSUInteger ) indexPath . row ;
Contact * contact = contacts [ row ] ;
2015-01-27 02:20:11 +01:00
cell . textLabel . attributedText = [ self attributedStringForContact : contact inCell : cell ] ;
2015-12-22 12:45:09 +01:00
2014-10-29 21:58:58 +01:00
tableView . tableFooterView = [ [ UIView alloc ] initWithFrame : CGRectZero ] ;
2015-12-22 12:45:09 +01:00
2015-02-17 00:14:50 +01:00
if ( [ [ tableView indexPathsForSelectedRows ] containsObject : indexPath ] ) {
[ self adjustSelected : cell ] ;
}
2015-12-22 12:45:09 +01:00
2014-10-29 21:58:58 +01:00
return cell ;
}
# pragma mark - Table View delegate
2015-12-22 12:45:09 +01:00
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath {
UITableViewCell * cell = [ tableView cellForRowAtIndexPath : indexPath ] ;
2015-02-17 00:14:50 +01:00
[ self adjustSelected : cell ] ;
2014-10-29 21:58:58 +01:00
}
2015-12-22 12:45:09 +01:00
- ( void ) adjustSelected : ( UITableViewCell * ) cell {
2015-02-17 00:14:50 +01:00
cell . accessoryType = UITableViewCellAccessoryCheckmark ;
}
2014-10-29 21:58:58 +01:00
2015-12-22 12:45:09 +01:00
- ( void ) tableView : ( UITableView * ) tableView didDeselectRowAtIndexPath : ( NSIndexPath * ) indexPath {
UITableViewCell * cell = [ tableView cellForRowAtIndexPath : indexPath ] ;
cell . accessoryType = UITableViewCellAccessoryNone ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Text Field Delegate
2015-12-22 12:45:09 +01:00
- ( BOOL ) textFieldShouldReturn : ( UITextField * ) textField {
2014-10-29 21:58:58 +01:00
[ self . nameGroupTextField resignFirstResponder ] ;
return NO ;
}
2014-11-21 14:38:37 +01:00
# pragma mark - Cell Utility
2015-12-22 12:45:09 +01:00
- ( NSAttributedString * ) attributedStringForContact : ( Contact * ) contact inCell : ( UITableViewCell * ) cell {
NSMutableAttributedString * fullNameAttributedString =
[ [ NSMutableAttributedString alloc ] initWithString : contact . fullName ] ;
2014-11-21 14:38:37 +01:00
UIFont * firstNameFont ;
UIFont * lastNameFont ;
2015-12-22 12:45:09 +01:00
2014-11-21 14:38:37 +01:00
if ( ABPersonGetSortOrdering ( ) = = kABPersonCompositeNameFormatFirstNameFirst ) {
2015-12-22 12:45:09 +01:00
firstNameFont = [ UIFont ows_mediumFontWithSize : cell . textLabel . font . pointSize ] ;
2015-01-14 22:30:01 +01:00
lastNameFont = [ UIFont ows_regularFontWithSize : cell . textLabel . font . pointSize ] ;
2015-12-22 12:45:09 +01:00
} else {
2015-01-14 22:30:01 +01:00
firstNameFont = [ UIFont ows_regularFontWithSize : cell . textLabel . font . pointSize ] ;
lastNameFont = [ UIFont ows_mediumFontWithSize : cell . textLabel . font . pointSize ] ;
2014-11-21 14:38:37 +01:00
}
2015-12-22 12:45:09 +01:00
[ fullNameAttributedString addAttribute : NSFontAttributeName
value : firstNameFont
range : NSMakeRange ( 0 , contact . firstName . length ) ] ;
[ fullNameAttributedString addAttribute : NSFontAttributeName
value : lastNameFont
range : NSMakeRange ( contact . firstName . length + 1 , contact . lastName . length ) ] ;
[ fullNameAttributedString addAttribute : NSForegroundColorAttributeName
value : [ UIColor blackColor ]
range : NSMakeRange ( 0 , contact . fullName . length ) ] ;
2015-01-14 22:30:01 +01:00
if ( ABPersonGetSortOrdering ( ) = = kABPersonCompositeNameFormatFirstNameFirst ) {
2015-12-22 12:45:09 +01:00
[ fullNameAttributedString addAttribute : NSForegroundColorAttributeName
value : [ UIColor ows_darkGrayColor ]
range : NSMakeRange ( contact . firstName . length + 1 , contact . lastName . length ) ] ;
} else {
[ fullNameAttributedString addAttribute : NSForegroundColorAttributeName
value : [ UIColor ows_darkGrayColor ]
range : NSMakeRange ( 0 , contact . firstName . length ) ] ;
2015-01-14 22:30:01 +01:00
}
2015-12-22 12:45:09 +01:00
2014-11-21 14:38:37 +01:00
return fullNameAttributedString ;
}
2014-10-29 21:58:58 +01:00
@ end