AddContactViewController.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2012-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "AddContactViewController.h"
  21. #import "ScanIdentityController.h"
  22. #import "ProtocolDefines.h"
  23. #import "ContactStore.h"
  24. #import "Contact.h"
  25. #import "InviteController.h"
  26. #import "MBProgressHUD.h"
  27. #import "NewScannedContactViewController.h"
  28. #import "AppDelegate.h"
  29. #import "GatewayAvatarMaker.h"
  30. #import "UIImage+ColoredImage.h"
  31. #import "NSString+Hex.h"
  32. #import "ServerAPIConnector.h"
  33. #import "ThreemaFramework/ThreemaFramework-swift.h"
  34. #import "MyIdentityStore.h"
  35. @interface AddContactViewController ()
  36. @end
  37. @implementation AddContactViewController {
  38. InviteController *inviteController;
  39. }
  40. - (void)viewDidLoad {
  41. [super viewDidLoad];
  42. if (![ScanIdentityController canScan]) {
  43. self.scanIdentityCell.selectionStyle = UITableViewCellSelectionStyleNone;
  44. self.scanIdentityCell.imageView.alpha = 0.4;
  45. self.scanIdentityCell.textLabel.alpha = 0.4;
  46. }
  47. // Tint icon appropriately
  48. self.scanIdentityCell.imageView.image = [self.scanIdentityCell.imageView.image imageWithTint:[Colors main]];
  49. [Colors updateKeyboardAppearanceFor:self.identityTextField];
  50. [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationDidEnterBackground:) name: UIApplicationDidEnterBackgroundNotification object: nil];
  51. }
  52. - (void)viewDidAppear:(BOOL)animated {
  53. [super viewDidAppear:animated];
  54. [self.identityTextField becomeFirstResponder];
  55. }
  56. - (IBAction)cancelAction:(id)sender {
  57. [self dismissViewControllerAnimated:YES completion:nil];
  58. }
  59. - (IBAction)doneAction:(id)sender {
  60. [self doAdd];
  61. }
  62. - (BOOL)shouldAutorotate {
  63. return YES;
  64. }
  65. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  66. if (SYSTEM_IS_IPAD) {
  67. return UIInterfaceOrientationMaskAll;
  68. }
  69. return UIInterfaceOrientationMaskAllButUpsideDown;
  70. }
  71. #pragma mark - Text field delegate
  72. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  73. if (indexPath.section == 1 && indexPath.row == 0) {
  74. /* Scan */
  75. if ([ScanIdentityController canScan]) {
  76. ScanIdentityController *scanController = [[ScanIdentityController alloc] init];
  77. scanController.containingViewController = self.presentingViewController;
  78. [self dismissViewControllerAnimated:YES completion:^{
  79. [scanController startScan];
  80. /* a good opportunity to sync contacts - maybe we find the contact
  81. that the user is about to scan */
  82. [[ContactStore sharedContactStore] synchronizeAddressBookForceFullSync:YES onCompletion:nil onError:nil];
  83. }];
  84. }
  85. } else if (indexPath.section == 1 && indexPath.row == 1) {
  86. [_identityTextField resignFirstResponder];
  87. /* Invite a friend */
  88. [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
  89. inviteController = [[InviteController alloc] init];
  90. inviteController.parentViewController = self.presentingViewController;
  91. inviteController.shareViewController = self;
  92. inviteController.actionSheetViewController = self;
  93. inviteController.rect = [tableView rectForRowAtIndexPath:indexPath];
  94. inviteController.delegate = self;
  95. inviteController.rect = [tableView rectForRowAtIndexPath:indexPath];
  96. [inviteController invite];
  97. }
  98. }
  99. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  100. // Check if the added string contains lowercase characters.
  101. // If so, those characters are replaced by uppercase characters.
  102. // But this has the effect of losing the editing point
  103. // (only when trying to edit with lowercase characters),
  104. // because the text of the UITextField is modified.
  105. // That is why we only replace the text when this is really needed.
  106. NSRange lowercaseCharRange;
  107. lowercaseCharRange = [string rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]];
  108. if (lowercaseCharRange.location != NSNotFound) {
  109. // Get current cursor position
  110. UITextRange *selectedTextRangeCurrent = textField.selectedTextRange;
  111. textField.text = [textField.text stringByReplacingCharactersInRange:range
  112. withString:[string uppercaseString]];
  113. if (selectedTextRangeCurrent != nil) {
  114. // Set current cursor position, if cursor position + 1 is valid
  115. UITextPosition *newPosition = [textField positionFromPosition:selectedTextRangeCurrent.start offset:1];
  116. if (newPosition != nil) {
  117. textField.selectedTextRange = [textField textRangeFromPosition:newPosition toPosition:newPosition];
  118. }
  119. }
  120. self.navigationItem.rightBarButtonItem.enabled = (textField.text.length == kIdentityLen);
  121. return NO;
  122. }
  123. NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
  124. self.navigationItem.rightBarButtonItem.enabled = (newText.length == kIdentityLen);
  125. return YES;
  126. }
  127. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  128. return [self doAdd];
  129. }
  130. - (BOOL)doAdd {
  131. NSString *myIdentity = MyIdentityStore.sharedMyIdentityStore.identity;
  132. if (self.identityTextField.text.length != kIdentityLen || [self.identityTextField.text isEqualToString:myIdentity])
  133. return NO;
  134. [MBProgressHUD showHUDAddedTo:self.view animated:YES];
  135. ServerAPIConnector *conn = [[ServerAPIConnector alloc] init];
  136. [conn fetchIdentityInfo:self.identityTextField.text onCompletion:^(NSData *publicKey, NSNumber *state, NSNumber *type, NSNumber *featureMask) {
  137. [MBProgressHUD hideHUDForView:self.view animated:YES];
  138. UIViewController *presentingViewController = self.presentingViewController;
  139. [self dismissViewControllerAnimated:YES completion:^{
  140. UIStoryboard *storyboard = [AppDelegate getMainStoryboard];
  141. UINavigationController *newNavVc = [storyboard instantiateViewControllerWithIdentifier:@"NewScannedContact"];
  142. NewScannedContactViewController *newVc = [newNavVc.viewControllers objectAtIndex:0];
  143. newVc.identity = self.identityTextField.text;
  144. newVc.publicKey = publicKey;
  145. newVc.verificationLevel = kVerificationLevelUnverified;
  146. newVc.state = state;
  147. newVc.type = type;
  148. newVc.featureMask = featureMask;
  149. [presentingViewController presentViewController:newNavVc animated:YES completion:nil];
  150. }];
  151. } onError:^(NSError *error) {
  152. [MBProgressHUD hideHUDForView:self.view animated:YES];
  153. if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == 404) {
  154. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"identity_not_found_title", nil) message:NSLocalizedString(@"identity_not_found_message", nil) actionOk:nil];
  155. } else {
  156. [UIAlertTemplate showAlertWithOwner:self title:error.localizedDescription message:error.localizedFailureReason actionOk:nil];
  157. }
  158. }];
  159. return YES;
  160. }
  161. #pragma mark - Invite controller delegate
  162. - (BOOL)inviteControllerShouldDeferMailComposer:(InviteController *)_inviteController {
  163. [self dismissViewControllerAnimated:YES completion:^{
  164. [inviteController presentMailComposer];
  165. }];
  166. return YES;
  167. }
  168. - (BOOL)inviteControllerShouldDeferMessageComposer:(InviteController *)_inviteController {
  169. [self dismissViewControllerAnimated:YES completion:^{
  170. [inviteController presentMessageComposer];
  171. }];
  172. return YES;
  173. }
  174. - (void)applicationDidEnterBackground:(UIApplication *)application {
  175. [_identityTextField resignFirstResponder];
  176. }
  177. @end