// // ContactDetailTableViewController.m // Signal // // Created by Dylan Bourgeois on 30/10/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. // #import "ContactDetailTableViewController.h" #import "ContactDetailCell.h" #import "ActionContactDetailCell.h" #import "UIUtil.h" #import "DJWActionSheet.h" #import "Environment.h" #import "PhoneManager.h" #define kImageRadius 50.0f #define kMinRows 3 #define kFirstAdaptableCellRow 2 typedef NS_ENUM(NSInteger, CellRow) { kNameMainNumberCellIndexPath, kActionCellIndexPath, kShareCellIndexPath, kEmailCellIndexPath, kAnnexPhoneNumberCellIndexPath, kNotesCellIndexPath, }; typedef enum { kNameMainNumberCellHeight = 180, kNoImageCellHeight = 87, kActionCellHeight = 60, kShareCellHeight = 60, kEmailCellHeight = 60, kAnnexPhoneNumberCellHeight = 60, kNotesCellHeight = 165, } kCellHeight; static NSString* const kNameMainNumberCell = @"NameMainNumberCell"; static NSString* const kActionCell = @"ActionCell"; static NSString* const kShareCell = @"ShareCell"; static NSString* const kEmailCell = @"EmailCell"; static NSString* const kAnnexPhoneNumberCell = @"AnnexPhoneNumberCell"; static NSString *const kNotesCell = @"NotesCell"; static NSString *const kContactDetailSegue = @"DetailSegue"; @interface ContactDetailTableViewController () { BOOL doesImageExist; NSInteger numberOfRows; } @end @implementation ContactDetailTableViewController - (void)viewDidLoad { [super viewDidLoad]; doesImageExist = YES; self.tableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return (NSInteger)[self numberOfRowsForContact:_contact]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; switch (indexPath.row) { case kNameMainNumberCellIndexPath: cell = (ContactDetailCell*)[tableView dequeueReusableCellWithIdentifier:kNameMainNumberCell forIndexPath:indexPath]; [self setUpNameMainUserCell:(ContactDetailCell*)cell]; break; case kActionCellIndexPath: cell = (ActionContactDetailCell*)[tableView dequeueReusableCellWithIdentifier:kActionCell forIndexPath:indexPath]; [self setUpActionCell:(ActionContactDetailCell*)cell]; break; case kShareCellIndexPath: cell = [tableView dequeueReusableCellWithIdentifier:kShareCell forIndexPath:indexPath]; break; default: cell = [self adaptableCellAtIndexPath:indexPath]; break; } return cell; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CGFloat cellHeight = 44.0f; switch (indexPath.row) { case kNameMainNumberCellIndexPath: cellHeight = doesImageExist ? kNameMainNumberCellHeight : kNoImageCellHeight; break; case kActionCellIndexPath: cellHeight = kActionCellHeight; break; case kShareCellIndexPath: cellHeight = kShareCellHeight; break; default: cellHeight = [self heightForAdaptableCellAtIndexPath:indexPath]; break; } return cellHeight; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.row) { case kShareCellIndexPath: [DJWActionSheet showInView:self.tabBarController.view withTitle:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@[@"Mail", @"Message", @"Airdrop", @"Other"] tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (tappedButtonIndex == actionSheet.cancelButtonIndex) { NSLog(@"User Cancelled"); } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { NSLog(@"Destructive button tapped"); }else { NSLog(@"The user tapped button at index: %li", (long)tappedButtonIndex); } }]; break; } } #pragma mark - Set Up Cells -(void)setUpActionCell:(ActionContactDetailCell*)cell { Contact * c = self.contact; UIImage *callImage = [[UIImage imageNamed:@"call_dark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [cell.contactCallButton setImage:callImage forState:UIControlStateNormal]; UIImage *messageImage = [[UIImage imageNamed:@"signals_tab"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [cell.contactTextButton setImage:messageImage forState:UIControlStateNormal]; UIImage *clearImage = [[UIImage imageNamed:@"delete_history"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [cell.contactShredButton setImage:clearImage forState:UIControlStateNormal]; cell.contactShredButton.tintColor = [UIColor redColor]; if (c.isRedPhoneContact) { cell.contactCallButton.tintColor = [UIColor colorWithRed:0.f/255.f green:122.f/255.f blue:255.f/255.f alpha:1.0f]; [cell.contactCallButton addTarget:self action:@selector(initiateRedPhoneCall) forControlEvents:UIControlEventTouchUpInside]; } else { cell.contactCallButton.tintColor = [UIColor colorWithRed:81.f/255.f green:81.f/255.f blue:81.f/255.f alpha:1.0f]; cell.contactCallButton.enabled = NO; } if (c.isTextSecureContact) { cell.contactTextButton.tintColor = [UIColor colorWithRed:0.f/255.f green:122.f/255.f blue:255.f/255.f alpha:1.0f]; [cell.contactTextButton addTarget:self action:@selector(openTextSecureConversation) forControlEvents:UIControlEventTouchUpInside]; } else { cell.contactTextButton.tintColor = [UIColor colorWithRed:81.f/255.f green:81.f/255.f blue:81.f/255.f alpha:1.0f]; cell.contactTextButton.enabled = NO; } } - (void)openTextSecureConversation{ NSArray *textSecureIdentifiers = [self.contact textSecureIdentifiers]; if (textSecureIdentifiers.count > 1) { [DJWActionSheet showInView:self.tabBarController.view withTitle:@"What number would you like to message?" cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:textSecureIdentifiers tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) { DDLogVerbose(@"User Cancelled Call"); } else { [Environment messageIdentifier:[textSecureIdentifiers objectAtIndex:(NSUInteger)tappedButtonIndex]]; } }]; } else if (textSecureIdentifiers.count == 1){ [Environment messageIdentifier:[textSecureIdentifiers firstObject]]; } else{ DDLogWarn(@"Tried to intiate a call but contact has no RedPhone identifier"); } } - (void)initiateRedPhoneCall{ NSArray *redPhoneIdentifiers = [self.contact redPhoneIdentifiers]; if (redPhoneIdentifiers.count > 1) { NSMutableArray *e164 = [NSMutableArray array]; for (PhoneNumber *phoneNumber in redPhoneIdentifiers) { [e164 addObject:phoneNumber.toE164]; } [DJWActionSheet showInView:self.tabBarController.view withTitle:@"What number would you like to dial?" cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:e164 tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) { DDLogVerbose(@"User Cancelled Call"); } else { [Environment.phoneManager initiateOutgoingCallToContact:self.contact atRemoteNumber:[redPhoneIdentifiers objectAtIndex:(NSUInteger)tappedButtonIndex]]; } }]; } else if (redPhoneIdentifiers.count == 1){ [Environment.phoneManager initiateOutgoingCallToContact:self.contact atRemoteNumber:[redPhoneIdentifiers firstObject]]; } else{ DDLogWarn(@"Tried to intiate a call but contact has no RedPhone identifier"); } } -(void)setUpNameMainUserCell:(ContactDetailCell*)cell { Contact* c = self.contact; cell.contactName.text = [c fullName]; cell.contactPhoneNumber.text = [[c userTextPhoneNumbers] firstObject]; if (c.image) { cell.contactImageView.image = c.image; } else { [cell.contactImageView addConstraint:[NSLayoutConstraint constraintWithItem:cell.contactImageView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0]]; doesImageExist = NO; } [cell.contactImageView.layer setCornerRadius:kImageRadius]; [cell.contactImageView.layer setMasksToBounds:YES]; } -(void)setUpEmailCell:(ContactDetailCell*)cell forIndexPath:(NSIndexPath*)indexPath { cell.contactEmailLabel.text = [_contact.emails objectAtIndex:(NSUInteger)indexPath.row-kMinRows]; } -(void)setUpAnnexNumberCell:(ContactDetailCell*)cell forIndexPath:(NSIndexPath*)indexPath { NSInteger i = indexPath.row - [self emailUpperBound] ; cell.contactAnnexNumberLabel.text = [_contact.userTextPhoneNumbers objectAtIndex:(NSUInteger)i]; } -(void)setUpNotesCell:(ContactDetailCell*)cell { cell.contactNotesTextView.text = _contact.notes; } #pragma mark - Utilities (Adaptable Cells) -(UITableViewCell*)adaptableCellAtIndexPath:(NSIndexPath*)indexPath { ContactDetailCell * cell; if ([self isEmailIndexPath:indexPath]) { cell = [self.tableView dequeueReusableCellWithIdentifier:kEmailCell forIndexPath:indexPath]; [self setUpEmailCell:cell forIndexPath:indexPath]; return cell; } else if ([self isAnnexNumberIndexPath:indexPath]) { cell = [self.tableView dequeueReusableCellWithIdentifier:kAnnexPhoneNumberCell forIndexPath:indexPath]; [self setUpAnnexNumberCell:cell forIndexPath:indexPath]; return cell; } else if ([self isNotesIndexPath:indexPath]) { cell = [self.tableView dequeueReusableCellWithIdentifier:kNotesCell forIndexPath:indexPath]; [self setUpNotesCell:cell]; return cell; } else { return nil; } } -(CGFloat)heightForAdaptableCellAtIndexPath:(NSIndexPath*)indexPath { if ([self isEmailIndexPath:indexPath]) { return kEmailCellHeight; } else if ([self isAnnexNumberIndexPath:indexPath]) { return kAnnexPhoneNumberCellHeight; } else if ([self isNotesIndexPath:indexPath]) { return kNotesCellHeight; } else { return 44.0f; } } #pragma mark - IndexPaths -(BOOL)isEmailIndexPath:(NSIndexPath*)indexPath { return indexPath.row > kFirstAdaptableCellRow && indexPath.row <= [self emailUpperBound]; } -(BOOL)isAnnexNumberIndexPath:(NSIndexPath*)indexPath { return indexPath.row > [self emailUpperBound] && indexPath.row < [self phoneNumberUpperBound]; } -(BOOL)isNotesIndexPath:(NSIndexPath*)indexPath { return indexPath.row == (NSInteger)[self numberOfRowsForContact:_contact]-1; } #pragma mark - Utilities (Bounds) -(NSInteger)emailUpperBound { return (NSInteger)(kFirstAdaptableCellRow+_contact.emails.count); } -(NSInteger)phoneNumberUpperBound { return [self emailUpperBound] + (NSInteger)_contact.userTextPhoneNumbers.count; } -(NSUInteger)numberOfRowsForContact:(Contact*)contact { NSUInteger numNotes = contact.notes.length == 0 ? 0 : 1; NSUInteger numEmails = contact.emails.count; NSUInteger numPhoneNumbers = contact.userTextPhoneNumbers.count-1; return kMinRows + numEmails + numPhoneNumbers + numNotes; } @end