session-ios/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m
Michael Kirk 3754b6f264 Edit 1:1 contact details
// FREEBIE
2017-05-10 10:32:41 -04:00

955 lines
45 KiB
Objective-C

//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSConversationSettingsTableViewController.h"
#import "BlockListUIUtils.h"
#import "ContactsViewHelper.h"
#import "Environment.h"
#import "FingerprintViewController.h"
#import "OWSAvatarBuilder.h"
#import "OWSBlockingManager.h"
#import "OWSContactsManager.h"
#import "PhoneNumber.h"
#import "ShowGroupMembersViewController.h"
#import "Signal-Swift.h"
#import "UIFont+OWS.h"
#import "UIUtil.h"
#import "UIView+OWS.h"
#import "UpdateGroupViewController.h"
#import <25519/Curve25519.h>
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
#import <SignalServiceKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/OWSFingerprintBuilder.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h>
#import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSOutgoingMessage.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <SignalServiceKit/TSThread.h>
@import ContactsUI;
NS_ASSUME_NONNULL_BEGIN
@interface OWSConversationSettingsTableViewController () <ContactEditingDelegate, ContactsViewHelperDelegate>
@property (nonatomic) TSThread *thread;
@property (nonatomic) NSArray<NSNumber *> *disappearingMessagesDurations;
@property (nonatomic) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper;
@property (nonatomic, readonly) UIImageView *avatarView;
@property (nonatomic, readonly) UILabel *disappearingMessagesDurationLabel;
@end
#pragma mark -
@implementation OWSConversationSettingsTableViewController
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
[self commonInit];
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) {
return self;
}
[self commonInit];
return self;
}
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (!self) {
return self;
}
[self commonInit];
return self;
}
- (void)commonInit
{
_storageManager = [TSStorageManager sharedManager];
_contactsManager = [Environment getCurrent].contactsManager;
_messageSender = [Environment getCurrent].messageSender;
_blockingManager = [OWSBlockingManager sharedManager];
_contactsViewHelper = [ContactsViewHelper new];
_contactsViewHelper.delegate = self;
}
- (NSString *)threadName
{
NSString *threadName = self.thread.name;
if ([threadName isEqualToString:self.thread.contactIdentifier]) {
threadName =
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:self.thread.contactIdentifier];
} else if (threadName.length == 0 && [self isGroupThread]) {
threadName = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
}
return threadName;
}
- (BOOL)isGroupThread
{
return [self.thread isKindOfClass:[TSGroupThread class]];
}
- (void)configureWithThread:(TSThread *)thread
{
self.thread = thread;
[self updateEditButton];
}
- (void)updateEditButton
{
OWSAssert(self.thread);
if ([self.thread isKindOfClass:[TSContactThread class]]) {
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"EDIT_TXT", nil)
style:UIBarButtonItemStylePlain
target:self
action:@selector(didTapEditButton)];
}
}
- (void)didTapEditButton
{
if (![self.thread isKindOfClass:[TSContactThread class]]) {
DDLogError(@"%@ unexpected thread: %@ in %s", self.tag, self.thread, __PRETTY_FUNCTION__);
OWSAssert(NO);
return;
}
TSContactThread *contactThread = (TSContactThread *)self.thread;
[self.contactsViewHelper presentContactViewControllerForRecipientId:contactThread.contactIdentifier
fromViewController:self
editImmediately:YES];
}
#pragma mark - ContactEditingDelegate
- (void)didFinishEditingContact
{
DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__);
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - CNContactViewControllerDelegate
- (void)contactViewController:(CNContactViewController *)viewController
didCompleteWithContact:(nullable CNContact *)contact
{
DDLogDebug(@"%@ done editing contact.", self.tag);
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - ContactsViewHelperDelegate
- (void)contactsViewHelperDidUpdateContacts
{
[self updateTableContents];
}
#pragma mark - View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Translations
self.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen");
_disappearingMessagesDurationLabel = [UILabel new];
self.disappearingMessagesDurations = [OWSDisappearingMessagesConfiguration validDurationsSeconds];
self.disappearingMessagesConfiguration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:self.thread.uniqueId];
if (!self.disappearingMessagesConfiguration) {
self.disappearingMessagesConfiguration =
[[OWSDisappearingMessagesConfiguration alloc] initDefaultWithThreadId:self.thread.uniqueId];
}
[self updateTableContents];
}
- (void)updateTableContents
{
OWSTableContents *contents = [OWSTableContents new];
contents.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen");
__weak OWSConversationSettingsTableViewController *weakSelf = self;
// First section.
OWSTableSection *firstSection = [OWSTableSection new];
firstSection.customHeaderView = [self firstSectionHeader];
firstSection.customHeaderHeight = @(100.f);
if (!self.isGroupThread && self.thread.hasSafetyNumbers) {
[firstSection
addItem:[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *iconView = [self viewForIconWithName:@"ic_lock_outline"];
[cell.contentView addSubview:iconView];
[iconView autoVCenterInSuperview];
[iconView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:16.f];
UILabel *rowLabel = [UILabel new];
rowLabel.text = NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings");
rowLabel.textColor = [UIColor blackColor];
rowLabel.font = [UIFont ows_regularFontWithSize:17.f];
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[cell.contentView addSubview:rowLabel];
[rowLabel autoVCenterInSuperview];
[rowLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:iconView withOffset:16.f];
return cell;
}
actionBlock:^{
OWSConversationSettingsTableViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
FingerprintViewController *fingerprintViewController = [[UIStoryboard main]
instantiateViewControllerWithIdentifier:@"FingerprintViewController"];
OWSFingerprintBuilder *fingerprintBuilder =
[[OWSFingerprintBuilder alloc] initWithStorageManager:strongSelf.storageManager
contactsManager:strongSelf.contactsManager];
OWSFingerprint *fingerprint =
[fingerprintBuilder fingerprintWithTheirSignalId:strongSelf.thread.contactIdentifier];
[fingerprintViewController configureWithThread:strongSelf.thread
fingerprint:fingerprint
contactName:[strongSelf threadName]];
fingerprintViewController.dismissDelegate = strongSelf;
[strongSelf presentViewController:fingerprintViewController animated:YES completion:nil];
}]];
}
[firstSection
addItem:[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIView *topView = [UIView new];
[cell.contentView addSubview:topView];
[topView autoPinWidthToSuperview];
[topView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[topView autoSetDimension:ALDimensionHeight toSize:kOWSTable_DefaultCellHeight];
UIImageView *iconView = [self viewForIconWithName:@"table_ic_timer"];
[topView addSubview:iconView];
[iconView autoVCenterInSuperview];
[iconView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:16.f];
UILabel *rowLabel = [UILabel new];
rowLabel.text = NSLocalizedString(@"DISAPPEARING_MESSAGES", @"table cell label in conversation settings");
rowLabel.textColor = [UIColor blackColor];
rowLabel.font = [UIFont ows_regularFontWithSize:17.f];
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[topView addSubview:rowLabel];
[rowLabel autoVCenterInSuperview];
[rowLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:iconView withOffset:16.f];
UISwitch *switchView = [UISwitch new];
switchView.on = self.disappearingMessagesConfiguration.isEnabled;
[switchView addTarget:self
action:@selector(disappearingMessagesSwitchValueDidChange:)
forControlEvents:UIControlEventValueChanged];
[topView addSubview:switchView];
[switchView autoVCenterInSuperview];
[switchView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:16.f];
UILabel *subtitleLabel = [UILabel new];
subtitleLabel.text
= NSLocalizedString(@"DISAPPEARING_MESSAGES_DESCRIPTION", @"subheading in conversation settings");
subtitleLabel.textColor = [UIColor blackColor];
subtitleLabel.font = [UIFont ows_footnoteFont];
subtitleLabel.numberOfLines = 0;
subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.contentView addSubview:subtitleLabel];
[subtitleLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topView];
[subtitleLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:rowLabel];
[subtitleLabel autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:16.f];
return cell;
}
// TODO: We shouldn't hard-code a row height that will contain the cell content.
customRowHeight:108.f
actionBlock:nil]];
if (self.disappearingMessagesConfiguration.isEnabled) {
[firstSection
addItem:[OWSTableItem
itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIView *topView = [UIView new];
[cell.contentView addSubview:topView];
[topView autoPinWidthToSuperview];
[topView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[topView autoSetDimension:ALDimensionHeight toSize:kOWSTable_DefaultCellHeight];
UIImageView *iconView = [self viewForIconWithName:@"table_ic_hourglass_empty"];
[topView addSubview:iconView];
[iconView autoVCenterInSuperview];
[iconView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:16.f];
UILabel *rowLabel = self.disappearingMessagesDurationLabel;
[self updateDisappearingMessagesDurationLabel];
rowLabel.textColor = [UIColor blackColor];
rowLabel.font = [UIFont ows_footnoteFont];
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[topView addSubview:rowLabel];
[rowLabel autoVCenterInSuperview];
[rowLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:iconView withOffset:16.f];
UISlider *slider = [UISlider new];
slider.maximumValue = (float)(self.disappearingMessagesDurations.count - 1);
slider.minimumValue = 0;
slider.continuous = YES; // NO fires change event only once you let go
slider.value = self.disappearingMessagesConfiguration.durationIndex;
[slider addTarget:self
action:@selector(durationSliderDidChange:)
forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:slider];
[slider autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topView];
[slider autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:rowLabel];
[slider autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:16.f];
return cell;
}
// TODO: We shouldn't hard-code a row height that will contain the cell content.
customRowHeight:84.f
actionBlock:nil]];
}
[contents addSection:firstSection];
// Group settings section.
if (self.isGroupThread) {
NSArray *groupItems = @[
[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.textLabel.text
= NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings");
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f];
cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
actionBlock:^{
[weakSelf showUpdateGroupView:UpdateGroupMode_Default];
}],
[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.textLabel.text
= NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings");
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f];
cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
actionBlock:^{
[weakSelf didTapLeaveGroup];
}],
[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.textLabel.text
= NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings");
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f];
cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
actionBlock:^{
OWSConversationSettingsTableViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
ShowGroupMembersViewController *showGroupMembersViewController =
[[UIStoryboard main] instantiateViewControllerWithIdentifier:@"ShowGroupMembersViewController"];
[showGroupMembersViewController configWithThread:(TSGroupThread *)strongSelf.thread];
[strongSelf.navigationController pushViewController:showGroupMembersViewController animated:YES];
}],
];
[contents addSection:[OWSTableSection sectionWithTitle:NSLocalizedString(@"GROUP_MANAGEMENT_SECTION",
@"Conversation settings table section title")
items:groupItems]];
}
// Mute thread section.
OWSTableSection *muteSection = [OWSTableSection new];
[muteSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *iconView = [self viewForIconWithName:@"ic_mute_thread"];
[cell.contentView addSubview:iconView];
[iconView autoVCenterInSuperview];
[iconView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:16.f];
UILabel *rowLabel = [UILabel new];
rowLabel.text = NSLocalizedString(
@"CONVERSATION_SETTINGS_MUTE_LABEL", @"label for 'mute thread' cell in conversation settings");
rowLabel.textColor = [UIColor blackColor];
rowLabel.font = [UIFont ows_regularFontWithSize:17.f];
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[cell.contentView addSubview:rowLabel];
[rowLabel autoVCenterInSuperview];
[rowLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:iconView withOffset:16.f];
NSString *muteStatus = NSLocalizedString(
@"CONVERSATION_SETTINGS_MUTE_NOT_MUTED", @"Indicates that the current thread is not muted.");
NSDate *mutedUntilDate = self.thread.mutedUntilDate;
NSDate *now = [NSDate date];
if (mutedUntilDate != nil && [mutedUntilDate timeIntervalSinceDate:now] > 0) {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit calendarUnits = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents *muteUntilComponents = [calendar components:calendarUnits fromDate:mutedUntilDate];
NSDateComponents *nowComponents = [calendar components:calendarUnits fromDate:now];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
if (nowComponents.year != muteUntilComponents.year || nowComponents.month != muteUntilComponents.month
|| nowComponents.day != muteUntilComponents.day) {
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
} else {
[dateFormatter setDateStyle:NSDateFormatterNoStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
}
muteStatus =
[NSString stringWithFormat:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTED_UNTIL_FORMAT",
@"Indicates that this thread is muted until a given date or time. "
@"Embeds {{The date or time which the thread is muted until}}."),
[dateFormatter stringFromDate:mutedUntilDate]];
}
UILabel *statusLabel = [UILabel new];
statusLabel.textColor = [UIColor colorWithWhite:0.5f alpha:1.f];
statusLabel.font = [UIFont ows_regularFontWithSize:17.f];
statusLabel.text = muteStatus;
[cell.contentView addSubview:statusLabel];
[statusLabel autoVCenterInSuperview];
[statusLabel autoPinEdgeToSuperviewEdge:ALEdgeRight];
return cell;
}
customRowHeight:45.f
actionBlock:^{
[weakSelf showMuteUnmuteActionSheet];
}]];
muteSection.footerTitle
= NSLocalizedString(@"MUTE_BEHAVIOR_EXPLANATION", @"An explanation of the consequences of muting a thread.");
[contents addSection:muteSection];
// Block user section.
if (!self.isGroupThread) {
BOOL isBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.thread.contactIdentifier];
OWSTableItem *item = [OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [UITableViewCell new];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageView *iconView = [self viewForIconWithName:@"ic_block"];
[cell.contentView addSubview:iconView];
[iconView autoVCenterInSuperview];
[iconView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:16.f];
UILabel *rowLabel = [UILabel new];
rowLabel.text = NSLocalizedString(
@"CONVERSATION_SETTINGS_BLOCK_THIS_USER", @"table cell label in conversation settings");
rowLabel.textColor = [UIColor blackColor];
rowLabel.font = [UIFont ows_regularFontWithSize:17.f];
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[cell.contentView addSubview:rowLabel];
[rowLabel autoVCenterInSuperview];
[rowLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:iconView withOffset:16.f];
UISwitch *blockUserSwitch = [UISwitch new];
blockUserSwitch.on = isBlocked;
[blockUserSwitch addTarget:self
action:@selector(blockUserSwitchDidChange:)
forControlEvents:UIControlEventValueChanged];
cell.accessoryView = blockUserSwitch;
return cell;
}
actionBlock:nil];
OWSTableSection *section = [OWSTableSection sectionWithTitle:nil
items:@[
item,
]];
section.footerTitle = NSLocalizedString(
@"BLOCK_BEHAVIOR_EXPLANATION", @"An explanation of the consequences of blocking another user.");
[contents addSection:section];
}
self.contents = contents;
[self.tableView reloadData];
}
- (UIView *)firstSectionHeader
{
UIView *firstSectionHeader = [UIView new];
UIView *threadInfoView = [UIView new];
[firstSectionHeader addSubview:threadInfoView];
[threadInfoView autoPinWidthToSuperviewWithMargin:16.f];
[threadInfoView autoPinHeightToSuperviewWithMargin:16.f];
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:self.thread contactsManager:self.contactsManager];
OWSAssert(avatar);
const CGFloat kAvatarSize = 68.f;
UIImageView *avatarView = [[UIImageView alloc] initWithImage:avatar];
_avatarView = avatarView;
avatarView.layer.borderColor = UIColor.clearColor.CGColor;
avatarView.layer.masksToBounds = YES;
avatarView.layer.cornerRadius = kAvatarSize / 2.0f;
avatarView.contentMode = UIViewContentModeScaleAspectFill;
[threadInfoView addSubview:avatarView];
[avatarView autoVCenterInSuperview];
[avatarView autoPinEdgeToSuperviewEdge:ALEdgeLeft];
[avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize];
[avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize];
UIView *threadNameView = [UIView new];
[threadInfoView addSubview:threadNameView];
[threadNameView autoVCenterInSuperview];
[threadNameView autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:avatarView withOffset:16.f];
[threadNameView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:16.f];
UILabel *threadTitleLabel = [UILabel new];
threadTitleLabel.text = self.threadName;
threadTitleLabel.textColor = [UIColor blackColor];
threadTitleLabel.font = [UIFont ows_dynamicTypeTitle2Font];
threadTitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[threadNameView addSubview:threadTitleLabel];
[threadTitleLabel autoPinEdgeToSuperviewEdge:ALEdgeTop];
[threadTitleLabel autoPinEdgeToSuperviewEdge:ALEdgeLeft];
if (![self isGroupThread] && ![self.thread.name isEqualToString:self.thread.contactIdentifier]) {
NSString *subtitle =
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:self.thread.contactIdentifier];
UILabel *threadSubtitleLabel = [UILabel new];
threadSubtitleLabel.text = subtitle;
threadSubtitleLabel.textColor = [UIColor blackColor];
// TODO:
threadSubtitleLabel.font = [UIFont ows_regularFontWithSize:12.f];
threadSubtitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[threadNameView addSubview:threadSubtitleLabel];
[threadSubtitleLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
[threadSubtitleLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:threadTitleLabel];
[threadSubtitleLabel autoPinEdgeToSuperviewEdge:ALEdgeLeft];
} else {
[threadTitleLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
}
[firstSectionHeader
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(conversationNameTouched:)]];
firstSectionHeader.userInteractionEnabled = YES;
return firstSectionHeader;
}
- (void)conversationNameTouched:(UIGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized) {
if (self.isGroupThread) {
CGPoint location = [sender locationInView:self.avatarView];
if (CGRectContainsPoint(self.avatarView.bounds, location)) {
[self showUpdateGroupView:UpdateGroupMode_EditGroupAvatar];
} else {
[self showUpdateGroupView:UpdateGroupMode_EditGroupName];
}
} else {
// TODO: Edit 1:1 contact.
}
}
}
- (UIImageView *)viewForIconWithName:(NSString *)iconName
{
UIImage *icon = [UIImage imageNamed:iconName];
OWSAssert(icon);
UIImageView *iconView = [UIImageView new];
iconView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
iconView.tintColor = [UIColor blackColor];
iconView.contentMode = UIViewContentModeScaleToFill;
[iconView autoSetDimension:ALDimensionWidth toSize:32.f];
[iconView autoSetDimension:ALDimensionHeight toSize:32.f];
return iconView;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// In case we're dismissing a CNContactViewController which requires default system appearance
[UIUtil applySignalAppearence];
// HACK to unselect rows when swiping back
// http://stackoverflow.com/questions/19379510/uitableviewcell-doesnt-get-deselected-when-swiping-back-quickly
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.disappearingMessagesConfiguration.isNewRecord && !self.disappearingMessagesConfiguration.isEnabled) {
// don't save defaults, else we'll unintentionally save the configuration and notify the contact.
return;
}
if (self.disappearingMessagesConfiguration.dictionaryValueDidChange) {
[self.disappearingMessagesConfiguration save];
OWSDisappearingConfigurationUpdateInfoMessage *infoMessage =
[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
initWithTimestamp:[NSDate ows_millisecondTimeStamp]
thread:self.thread
configuration:self.disappearingMessagesConfiguration];
[infoMessage save];
[OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob
runWithConfiguration:self.disappearingMessagesConfiguration
thread:self.thread
messageSender:self.messageSender];
}
}
#pragma mark - Actions
- (void)showUpdateGroupView:(UpdateGroupMode)mode
{
OWSAssert(self.conversationSettingsViewDelegate);
UpdateGroupViewController *updateGroupViewController = [UpdateGroupViewController new];
updateGroupViewController.conversationSettingsViewDelegate = self.conversationSettingsViewDelegate;
updateGroupViewController.thread = (TSGroupThread *)self.thread;
updateGroupViewController.mode = mode;
UINavigationController *navigationController =
[[UINavigationController alloc] initWithRootViewController:updateGroupViewController];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)didTapLeaveGroup
{
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"CONFIRM_LEAVE_GROUP_TITLE", @"Alert title")
message:NSLocalizedString(@"CONFIRM_LEAVE_GROUP_DESCRIPTION", @"Alert body")
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *leaveAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"LEAVE_BUTTON_TITLE", @"Confirmation button within contextual alert")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[self leaveGroup];
}];
[alertController addAction:leaveAction];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- (void)leaveGroup
{
TSGroupThread *gThread = (TSGroupThread *)self.thread;
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:gThread
groupMetaMessage:TSGroupMessageQuit];
[self.messageSender sendMessage:message
success:^{
DDLogInfo(@"%@ Successfully left group.", self.tag);
}
failure:^(NSError *error) {
DDLogWarn(@"%@ Failed to leave group with error: %@", self.tag, error);
}];
NSMutableArray *newGroupMemberIds = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
[newGroupMemberIds removeObject:[self.storageManager localNumber]];
gThread.groupModel.groupMemberIds = newGroupMemberIds;
[gThread save];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)presentedModalWasDismissed
{
// Else row stays selected after dismissing modal.
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
- (void)disappearingMessagesSwitchValueDidChange:(UISwitch *)sender
{
UISwitch *disappearingMessagesSwitch = (UISwitch *)sender;
[self toggleDisappearingMessages:disappearingMessagesSwitch.isOn];
[self updateTableContents];
}
- (void)blockUserSwitchDidChange:(id)sender
{
OWSAssert(!self.isGroupThread);
if (![sender isKindOfClass:[UISwitch class]]) {
DDLogError(@"%@ Unexpected sender for block user switch: %@", self.tag, sender);
OWSAssert(0);
}
UISwitch *blockUserSwitch = (UISwitch *)sender;
BOOL isCurrentlyBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.thread.contactIdentifier];
if (blockUserSwitch.isOn) {
OWSAssert(!isCurrentlyBlocked);
if (isCurrentlyBlocked) {
return;
}
[BlockListUIUtils showBlockPhoneNumberActionSheet:self.thread.contactIdentifier
fromViewController:self
blockingManager:_blockingManager
contactsManager:_contactsManager
completionBlock:^(BOOL isBlocked) {
// Update switch state if user cancels action.
blockUserSwitch.on = isBlocked;
}];
} else {
OWSAssert(isCurrentlyBlocked);
if (!isCurrentlyBlocked) {
return;
}
[BlockListUIUtils showUnblockPhoneNumberActionSheet:self.thread.contactIdentifier
fromViewController:self
blockingManager:_blockingManager
contactsManager:_contactsManager
completionBlock:^(BOOL isBlocked) {
// Update switch state if user cancels action.
blockUserSwitch.on = isBlocked;
}];
}
}
- (void)toggleDisappearingMessages:(BOOL)flag
{
self.disappearingMessagesConfiguration.enabled = flag;
[self.tableView reloadData];
}
- (void)durationSliderDidChange:(UISlider *)slider
{
// snap the slider to a valid value
NSUInteger index = (NSUInteger)(slider.value + 0.5);
[slider setValue:index animated:YES];
NSNumber *numberOfSeconds = self.disappearingMessagesDurations[index];
self.disappearingMessagesConfiguration.durationSeconds = [numberOfSeconds unsignedIntValue];
[self updateDisappearingMessagesDurationLabel];
}
- (void)updateDisappearingMessagesDurationLabel
{
if (self.disappearingMessagesConfiguration.isEnabled) {
NSString *keepForFormat = NSLocalizedString(@"KEEP_MESSAGES_DURATION",
@"Slider label embeds {{TIME_AMOUNT}}, e.g. '2 hours'. See *_TIME_AMOUNT strings for examples.");
self.disappearingMessagesDurationLabel.text =
[NSString stringWithFormat:keepForFormat, self.disappearingMessagesConfiguration.durationString];
} else {
self.disappearingMessagesDurationLabel.text
= NSLocalizedString(@"KEEP_MESSAGES_FOREVER", @"Slider label when disappearing messages is off");
}
[self.disappearingMessagesDurationLabel setNeedsLayout];
[self.disappearingMessagesDurationLabel.superview setNeedsLayout];
}
- (void)showMuteUnmuteActionSheet
{
// The "unmute" action sheet has no title or message; the
// action label speaks for itself.
NSString *title = nil;
NSString *message = nil;
if (!self.thread.isMuted) {
title = NSLocalizedString(
@"CONVERSATION_SETTINGS_MUTE_ACTION_SHEET_TITLE", @"Title of the 'mute this thread' action sheet.");
message = NSLocalizedString(
@"MUTE_BEHAVIOR_EXPLANATION", @"An explanation of the consequences of muting a thread.");
}
UIAlertController *actionSheetController =
[UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleActionSheet];
__weak OWSConversationSettingsTableViewController *weakSelf = self;
if (self.thread.isMuted) {
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_UNMUTE_ACTION",
@"Label for button to unmute a thread.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
[weakSelf setThreadMutedUntilDate:nil];
}];
[actionSheetController addAction:action];
} else {
#ifdef DEBUG
[actionSheetController
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_ONE_MINUTE_ACTION",
@"Label for button to mute a thread for a minute.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *dateComponents = [NSDateComponents new];
[dateComponents setMinute:1];
NSDate *mutedUntilDate =
[calendar dateByAddingComponents:dateComponents
toDate:[NSDate date]
options:0];
[weakSelf setThreadMutedUntilDate:mutedUntilDate];
}]];
#endif
[actionSheetController
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_ONE_HOUR_ACTION",
@"Label for button to mute a thread for a hour.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *dateComponents = [NSDateComponents new];
[dateComponents setHour:1];
NSDate *mutedUntilDate =
[calendar dateByAddingComponents:dateComponents
toDate:[NSDate date]
options:0];
[weakSelf setThreadMutedUntilDate:mutedUntilDate];
}]];
[actionSheetController
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_ONE_DAY_ACTION",
@"Label for button to mute a thread for a day.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *dateComponents = [NSDateComponents new];
[dateComponents setDay:1];
NSDate *mutedUntilDate =
[calendar dateByAddingComponents:dateComponents
toDate:[NSDate date]
options:0];
[weakSelf setThreadMutedUntilDate:mutedUntilDate];
}]];
[actionSheetController
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_ONE_WEEK_ACTION",
@"Label for button to mute a thread for a week.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *dateComponents = [NSDateComponents new];
[dateComponents setDay:7];
NSDate *mutedUntilDate =
[calendar dateByAddingComponents:dateComponents
toDate:[NSDate date]
options:0];
[weakSelf setThreadMutedUntilDate:mutedUntilDate];
}]];
[actionSheetController
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_ONE_YEAR_ACTION",
@"Label for button to mute a thread for a year.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull ignore) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *dateComponents = [NSDateComponents new];
[dateComponents setYear:1];
NSDate *mutedUntilDate =
[calendar dateByAddingComponents:dateComponents
toDate:[NSDate date]
options:0];
[weakSelf setThreadMutedUntilDate:mutedUntilDate];
}]];
}
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
style:UIAlertActionStyleCancel
handler:nil];
[actionSheetController addAction:dismissAction];
[self presentViewController:actionSheetController animated:YES completion:nil];
}
- (void)setThreadMutedUntilDate:(nullable NSDate *)value
{
[self.thread updateWithMutedUntilDate:value];
[self.tableView reloadData];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END