Show verification state banner. Show verification state in conversation settings view.

// FREEBIE
This commit is contained in:
Matthew Chen 2017-06-09 16:21:59 -04:00
parent 8db75e86a1
commit 11ca51c95f
14 changed files with 449 additions and 108 deletions

View File

@ -81,6 +81,7 @@
34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; };
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
34E8BF381EE9E2FD00F5F4CA /* FingerprintViewScanController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8BF371EE9E2FD00F5F4CA /* FingerprintViewScanController.m */; };
34E8BF3B1EEB208E00F5F4CA /* DebugUIVerification.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8BF3A1EEB208E00F5F4CA /* DebugUIVerification.m */; };
34F3089C1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */; };
34F3089F1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */; };
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; };
@ -494,6 +495,8 @@
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
34E8BF361EE9E2FD00F5F4CA /* FingerprintViewScanController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FingerprintViewScanController.h; sourceTree = "<group>"; };
34E8BF371EE9E2FD00F5F4CA /* FingerprintViewScanController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FingerprintViewScanController.m; sourceTree = "<group>"; };
34E8BF391EEB208E00F5F4CA /* DebugUIVerification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIVerification.h; sourceTree = "<group>"; };
34E8BF3A1EEB208E00F5F4CA /* DebugUIVerification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIVerification.m; sourceTree = "<group>"; };
34F3089A1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSUnreadIndicatorInteraction.h; sourceTree = "<group>"; };
34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSUnreadIndicatorInteraction.m; sourceTree = "<group>"; };
34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnreadIndicatorCell.h; sourceTree = "<group>"; };
@ -1030,10 +1033,12 @@
34D8C02A1ED3685800188D7C /* DebugUIContacts.m */,
34D8C0231ED3673300188D7C /* DebugUIMessages.h */,
34D8C0241ED3673300188D7C /* DebugUIMessages.m */,
34D8C0251ED3673300188D7C /* DebugUITableViewController.h */,
34D8C0261ED3673300188D7C /* DebugUITableViewController.m */,
452037CF1EE84975004E4CDF /* DebugUISessionState.h */,
452037D01EE84975004E4CDF /* DebugUISessionState.m */,
34D8C0251ED3673300188D7C /* DebugUITableViewController.h */,
34D8C0261ED3673300188D7C /* DebugUITableViewController.m */,
34E8BF391EEB208E00F5F4CA /* DebugUIVerification.h */,
34E8BF3A1EEB208E00F5F4CA /* DebugUIVerification.m */,
);
path = DebugUI;
sourceTree = "<group>";
@ -2099,6 +2104,7 @@
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */,
76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */,
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */,
34E8BF3B1EEB208E00F5F4CA /* DebugUIVerification.m in Sources */,
76EB058A18170B33006006FC /* Release.m in Sources */,
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,

View File

@ -79,18 +79,18 @@ NS_ASSUME_NONNULL_BEGIN
- (void)signalAccountsDidChange:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self updateContacts];
});
OWSAssert([NSThread isMainThread]);
[self updateContacts];
}
- (void)blockedPhoneNumbersDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
self.blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers];
OWSAssert([NSThread isMainThread]);
[self updateContacts];
});
self.blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers];
[self updateContacts];
}
#pragma mark - Contacts

View File

@ -182,7 +182,7 @@ typedef enum : NSUInteger {
@property (nonatomic) UILabel *navigationBarTitleLabel;
@property (nonatomic) UILabel *navigationBarSubtitleLabel;
@property (nonatomic) UIButton *attachButton;
@property (nonatomic) UIView *blockStateIndicator;
@property (nonatomic) UIView *bannerView;
// Back Button Unread Count
@property (nonatomic, readonly) UIView *backButtonUnreadCountView;
@ -294,9 +294,9 @@ typedef enum : NSUInteger {
- (void)blockedPhoneNumbersDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self ensureBlockStateIndicator];
});
OWSAssert([NSThread isMainThread]);
[self ensureBannerState];
}
- (void)identityStateDidChange:(NSNotification *)notification
@ -304,6 +304,7 @@ typedef enum : NSUInteger {
OWSAssert([NSThread isMainThread]);
[self updateNavigationBarSubtitleLabel];
[self ensureBannerState];
}
- (void)peekSetup
@ -543,7 +544,7 @@ typedef enum : NSUInteger {
// Triggering modified notification renders "call notification" when leaving full screen call view
[self.thread touch];
[self ensureBlockStateIndicator];
[self ensureBannerState];
[self resetContentAndLayout];
@ -671,20 +672,52 @@ typedef enum : NSUInteger {
{
_userHasScrolled = userHasScrolled;
[self ensureBlockStateIndicator];
[self ensureBannerState];
}
- (void)ensureBlockStateIndicator
- (void)ensureBannerState
{
// This method should be called rarely, so it's simplest to discard and
// rebuild the indicator view every time.
[self.blockStateIndicator removeFromSuperview];
self.blockStateIndicator = nil;
[self.bannerView removeFromSuperview];
self.bannerView = nil;
if (self.userHasScrolled) {
return;
}
// A collection of the group members who are "no longer verified".
NSMutableArray<NSString *> *noLongerVerifiedRecipientIds = [NSMutableArray new];
for (NSString *recipientId in self.thread.recipientIdentifiers) {
if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId]
== OWSVerificationStateNoLongerVerified) {
[noLongerVerifiedRecipientIds addObject:recipientId];
}
}
if (noLongerVerifiedRecipientIds.count > 0) {
NSString *message;
if (noLongerVerifiedRecipientIds.count > 1) {
message = NSLocalizedString(@"MESSAGES_VIEW_N_MEMBERS_NO_LONGER_VERIFIED",
@"Indicates that more than one member of this group conversation is no longer verified.");
} else {
NSString *recipientId = [noLongerVerifiedRecipientIds firstObject];
NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:recipientId];
NSString *format
= (self.isGroupConversation ? NSLocalizedString(@"MESSAGES_VIEW_1_MEMBER_NO_LONGER_VERIFIED_FORMAT",
@"Indicates that one member of this group conversation is no longer "
@"verified. Embeds {{user's name or phone number}}.")
: NSLocalizedString(@"MESSAGES_VIEW_CONTACT_NO_LONGER_VERIFIED_FORMAT",
@"Indicates that this 1:1 conversation is no longer verified. Embeds "
@"{{user's name or phone number}}."));
message = [NSString stringWithFormat:format, displayName];
}
[self createBannerWithTitle:message
bannerColor:[UIColor ows_destructiveRedColor]
tapSelector:@selector(noLongerVerifiedBannerViewWasTapped:)];
return;
}
NSString *blockStateMessage = nil;
if ([self isBlockedContactConversation]) {
blockStateMessage = NSLocalizedString(
@ -704,41 +737,61 @@ typedef enum : NSUInteger {
}
if (blockStateMessage) {
UILabel *label = [UILabel new];
label.font = [UIFont ows_mediumFontWithSize:14.f];
label.text = blockStateMessage;
label.textColor = [UIColor whiteColor];
UIView *blockStateIndicator = [UIView new];
blockStateIndicator.backgroundColor = [UIColor ows_redColor];
blockStateIndicator.layer.cornerRadius = 2.5f;
// Use a shadow to "pop" the indicator above the other views.
blockStateIndicator.layer.shadowColor = [UIColor blackColor].CGColor;
blockStateIndicator.layer.shadowOffset = CGSizeMake(2, 3);
blockStateIndicator.layer.shadowRadius = 2.f;
blockStateIndicator.layer.shadowOpacity = 0.35f;
[blockStateIndicator addSubview:label];
[label autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:5];
[label autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:5];
[label autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:15];
[label autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:15];
[blockStateIndicator addGestureRecognizer:[[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(blockStateIndicatorWasTapped:)]];
[self.view addSubview:blockStateIndicator];
[blockStateIndicator autoHCenterInSuperview];
[blockStateIndicator autoPinToTopLayoutGuideOfViewController:self withInset:10];
[self.view layoutSubviews];
self.blockStateIndicator = blockStateIndicator;
[self createBannerWithTitle:blockStateMessage
bannerColor:[UIColor ows_destructiveRedColor]
tapSelector:@selector(blockBannerViewWasTapped:)];
}
}
- (void)blockStateIndicatorWasTapped:(UIGestureRecognizer *)sender
- (void)createBannerWithTitle:(NSString *)title bannerColor:(UIColor *)bannerColor tapSelector:(SEL)tapSelector
{
OWSAssert(title.length > 0);
OWSAssert(bannerColor);
UILabel *label = [UILabel new];
label.font = [UIFont ows_mediumFontWithSize:14.f];
label.text = title;
label.textColor = [UIColor whiteColor];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.textAlignment = NSTextAlignmentCenter;
UIView *bannerView = [UIView new];
bannerView.backgroundColor = bannerColor;
bannerView.layer.cornerRadius = 2.5f;
// Use a shadow to "pop" the indicator above the other views.
bannerView.layer.shadowColor = [UIColor blackColor].CGColor;
bannerView.layer.shadowOffset = CGSizeMake(2, 3);
bannerView.layer.shadowRadius = 2.f;
bannerView.layer.shadowOpacity = 0.35f;
[bannerView addSubview:label];
[label autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:5];
[label autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:5];
const CGFloat kBannerHPadding = 15.f;
[label autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:kBannerHPadding];
[label autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:kBannerHPadding];
[bannerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:tapSelector]];
[self.view addSubview:bannerView];
[bannerView autoPinToTopLayoutGuideOfViewController:self withInset:10];
[bannerView autoHCenterInSuperview];
CGFloat labelDesiredWidth = [label sizeThatFits:CGSizeZero].width;
CGFloat bannerDesiredWidth = labelDesiredWidth + kBannerHPadding * 2.f;
const CGFloat kMinBannerHMargin = 20.f;
if (bannerDesiredWidth + kMinBannerHMargin * 2.f >= self.view.width) {
[bannerView autoPinWidthToSuperviewWithMargin:kMinBannerHMargin];
}
[self.view layoutSubviews];
self.bannerView = bannerView;
}
- (void)blockBannerViewWasTapped:(UIGestureRecognizer *)sender
{
if (sender.state != UIGestureRecognizerStateRecognized) {
return;
@ -758,6 +811,13 @@ typedef enum : NSUInteger {
}
}
- (void)noLongerVerifiedBannerViewWasTapped:(UIGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized) {
[self showConversationSettingsAndShowVerification:YES];
}
}
- (void)showUnblockContactUI:(BlockActionCompletionBlock)completionBlock
{
OWSAssert([self.thread isKindOfClass:[TSContactThread class]]);
@ -1792,6 +1852,11 @@ typedef enum : NSUInteger {
#pragma mark - Actions
- (void)showConversationSettings
{
[self showConversationSettingsAndShowVerification:NO];
}
- (void)showConversationSettingsAndShowVerification:(BOOL)showVerification
{
if (self.userLeftGroup) {
DDLogDebug(@"%@ Ignoring request to show conversation settings, since user left group", self.tag);
@ -1801,6 +1866,7 @@ typedef enum : NSUInteger {
OWSConversationSettingsTableViewController *settingsVC = [OWSConversationSettingsTableViewController new];
settingsVC.conversationSettingsViewDelegate = self;
[settingsVC configureWithThread:self.thread];
settingsVC.showVerificationOnAppear = showVerification;
[self.navigationController pushViewController:settingsVC animated:YES];
}

View File

@ -6,6 +6,7 @@
#import "DebugUIContacts.h"
#import "DebugUIMessages.h"
#import "DebugUISessionState.h"
#import "DebugUIVerification.h"
#import "Signal-Swift.h"
#import <SignalServiceKit/TSContactThread.h>
#import <SignalServiceKit/TSThread.h>
@ -38,41 +39,43 @@ NS_ASSUME_NONNULL_BEGIN
[self.navigationController pushViewController:viewController animated:YES];
}
+ (OWSTableItem *)itemForSubsection:(OWSTableSection *)section
viewController:(DebugUITableViewController *)viewController
{
OWSAssert(section);
__weak DebugUITableViewController *weakSelf = viewController;
return [OWSTableItem disclosureItemWithText:section.headerTitle
actionBlock:^{
[weakSelf pushPageWithSection:section];
}];
}
+ (void)presentDebugUIForThread:(TSThread *)thread fromViewController:(UIViewController *)fromViewController
{
OWSAssert(thread);
OWSAssert(fromViewController);
DebugUITableViewController *viewController = [DebugUITableViewController new];
__weak DebugUITableViewController *weakSelf = viewController;
OWSTableContents *contents = [OWSTableContents new];
contents.title = @"Debug: Conversation";
OWSTableSection *messagesSection = [DebugUIMessages sectionForThread:thread];
[contents addSection:[OWSTableSection
sectionWithTitle:messagesSection.headerTitle
items:@[
[OWSTableItem disclosureItemWithText:messagesSection.headerTitle
actionBlock:^{
[weakSelf pushPageWithSection:messagesSection];
}],
]]];
NSMutableArray<OWSTableItem *> *subsectionItems = [NSMutableArray new];
[subsectionItems
addObject:[self itemForSubsection:[DebugUIMessages sectionForThread:thread] viewController:viewController]];
[subsectionItems addObject:[self itemForSubsection:[DebugUIContacts section] viewController:viewController]];
if ([thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
OWSTableSection *sessionSection = [DebugUISessionState sectionForContactThread:contactThread];
[contents addSection:[OWSTableSection
sectionWithTitle:sessionSection.headerTitle
items:@[
[OWSTableItem disclosureItemWithText:sessionSection.headerTitle
actionBlock:^{
[weakSelf
pushPageWithSection:sessionSection];
}],
]]];
[subsectionItems addObject:[self itemForSubsection:[DebugUISessionState sectionForContactThread:contactThread]
viewController:viewController]];
[subsectionItems addObject:[self itemForSubsection:[DebugUIVerification sectionForThread:contactThread]
viewController:viewController]];
}
[contents addSection:[OWSTableSection sectionWithTitle:@"Sections" items:subsectionItems]];
if ([thread isKindOfClass:[TSContactThread class]]) {
// After enqueing the notification you may want to background the app or lock the screen before it triggers, so
// we give a little delay.
uint64_t notificationDelay = 5;
@ -138,8 +141,6 @@ NS_ASSUME_NONNULL_BEGIN
]]];
} // end contact thread section
[contents addSection:[DebugUIContacts section]];
viewController.contents = contents;
[viewController presentFromViewController:fromViewController];
}

View File

@ -0,0 +1,17 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSTableViewController.h"
NS_ASSUME_NONNULL_BEGIN
@class TSContactThread;
@interface DebugUIVerification : NSObject
+ (OWSTableSection *)sectionForThread:(TSContactThread *)thread;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,77 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "DebugUIVerification.h"
#import "DebugUIMessages.h"
#import "Signal-Swift.h"
#import <SignalServiceKit/OWSIdentityManager.h>
NS_ASSUME_NONNULL_BEGIN
@implementation DebugUIVerification
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
#pragma mark - Factory Methods
+ (OWSTableSection *)sectionForThread:(TSContactThread *)thread
{
OWSAssert(thread);
NSString *recipientId = thread.contactIdentifier;
OWSAssert(recipientId.length > 0);
return [OWSTableSection
sectionWithTitle:@"Verification"
items:@[
[OWSTableItem itemWithTitle:@"Default"
actionBlock:^{
[DebugUIVerification setVerificationState:OWSVerificationStateDefault
recipientId:recipientId];
}],
[OWSTableItem itemWithTitle:@"Verified"
actionBlock:^{
[DebugUIVerification setVerificationState:OWSVerificationStateVerified
recipientId:recipientId];
}],
[OWSTableItem itemWithTitle:@"No Longer Verified"
actionBlock:^{
[DebugUIVerification
setVerificationState:OWSVerificationStateNoLongerVerified
recipientId:recipientId];
}],
]];
}
+ (void)setVerificationState:(OWSVerificationState)verificationState recipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
OWSRecipientIdentity *_Nullable recipientIdentity =
[[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId];
OWSAssert(recipientIdentity);
// By capturing the identity key when we enter these views, we prevent the edge case
// where the user verifies a key that we learned about while this view was open.
NSData *identityKey = recipientIdentity.identityKey;
OWSAssert(identityKey.length > 0);
[OWSIdentityManager.sharedManager setVerificationState:verificationState
identityKey:identityKey
recipientId:recipientId
sendSyncMessage:verificationState != OWSVerificationStateNoLongerVerified];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<OWSConversationSettingsViewDelegate> conversationSettingsViewDelegate;
@property (nonatomic) BOOL showVerificationOnAppear;
- (void)configureWithThread:(TSThread *)thread;
@end

View File

@ -95,6 +95,21 @@ NS_ASSUME_NONNULL_BEGIN
_messageSender = [Environment getCurrent].messageSender;
_blockingManager = [OWSBlockingManager sharedManager];
_contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self];
[self observeNotifications];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(identityStateDidChange:)
name:kNSNotificationName_IdentityStateDidChange
object:nil];
}
- (NSString *)threadName
@ -196,6 +211,20 @@ NS_ASSUME_NONNULL_BEGIN
[self updateTableContents];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.showVerificationOnAppear) {
self.showVerificationOnAppear = NO;
if (self.isGroupThread) {
[self showGroupMembersView];
} else {
[self showVerificationView];
}
}
}
- (void)updateTableContents
{
OWSTableContents *contents = [OWSTableContents new];
@ -218,15 +247,7 @@ NS_ASSUME_NONNULL_BEGIN
iconName:@"table_ic_verify"];
}
actionBlock:^{
OWSConversationSettingsTableViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
FingerprintViewController *fingerprintViewController = [FingerprintViewController new];
[fingerprintViewController configureWithRecipientId:strongSelf.thread.contactIdentifier];
UINavigationController *navigationController =
[[UINavigationController alloc] initWithRootViewController:fingerprintViewController];
[strongSelf presentViewController:navigationController animated:YES completion:nil];
[weakSelf showVerificationView];
}]];
}
@ -349,14 +370,7 @@ NS_ASSUME_NONNULL_BEGIN
iconName:@"table_ic_group_members"];
}
actionBlock:^{
OWSConversationSettingsTableViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
ShowGroupMembersViewController *showGroupMembersViewController =
[ShowGroupMembersViewController new];
[showGroupMembersViewController configWithThread:(TSGroupThread *)strongSelf.thread];
[strongSelf.navigationController pushViewController:showGroupMembersViewController animated:YES];
[weakSelf showGroupMembersView];
}],
[OWSTableItem itemWithCustomCellBlock:^{
return [weakSelf disclosureCellWithName:NSLocalizedString(@"LEAVE_GROUP_ACTION",
@ -535,15 +549,47 @@ NS_ASSUME_NONNULL_BEGIN
[threadTitleLabel autoPinEdgeToSuperviewEdge:ALEdgeLeft];
[threadTitleLabel autoPinEdgeToSuperviewEdge:ALEdgeRight];
if (![self isGroupThread] && ![self.thread.name isEqualToString:self.thread.contactIdentifier]) {
NSString *subtitle =
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:self.thread.contactIdentifier];
const CGFloat kSubtitlePointSize = 12.f;
NSMutableAttributedString *subtitle = nil;
if (![self isGroupThread]) {
NSString *recipientId = self.thread.contactIdentifier;
BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId]
== OWSVerificationStateVerified;
BOOL hasName = ![self.thread.name isEqualToString:recipientId];
if (isVerified || hasName) {
subtitle = [NSMutableAttributedString new];
if (isVerified) {
// "checkmark"
[subtitle appendAttributedString:[[NSAttributedString alloc]
initWithString:@"\uf00c "
attributes:@{
NSFontAttributeName :
[UIFont ows_fontAwesomeFont:kSubtitlePointSize],
}]];
}
if (hasName) {
[subtitle
appendAttributedString:
[[NSAttributedString alloc]
initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:
recipientId]]];
} else {
[subtitle
appendAttributedString:[[NSAttributedString alloc]
initWithString:NSLocalizedString(@"PRIVACY_IDENTITY_IS_VERIFIED_BADGE",
@"Badge indicating that the user is verified.")]];
}
}
}
if (subtitle) {
UILabel *threadSubtitleLabel = [UILabel new];
threadSubtitleLabel.text = subtitle;
threadSubtitleLabel.textColor = [UIColor blackColor];
// TODO:
threadSubtitleLabel.font = [UIFont ows_regularFontWithSize:12.f];
threadSubtitleLabel.textColor = [UIColor ows_darkGrayColor];
threadSubtitleLabel.font = [UIFont ows_regularFontWithSize:kSubtitlePointSize];
threadSubtitleLabel.attributedText = subtitle;
threadSubtitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[threadNameView addSubview:threadSubtitleLabel];
[threadSubtitleLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
@ -631,6 +677,22 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Actions
- (void)showVerificationView
{
FingerprintViewController *fingerprintViewController = [FingerprintViewController new];
[fingerprintViewController configureWithRecipientId:self.thread.contactIdentifier];
UINavigationController *navigationController =
[[UINavigationController alloc] initWithRootViewController:fingerprintViewController];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)showGroupMembersView
{
ShowGroupMembersViewController *showGroupMembersViewController = [ShowGroupMembersViewController new];
[showGroupMembersViewController configWithThread:(TSGroupThread *)self.thread];
[self.navigationController pushViewController:showGroupMembersViewController animated:YES];
}
- (void)showUpdateGroupView:(UpdateGroupMode)mode
{
OWSAssert(self.conversationSettingsViewDelegate);
@ -927,6 +989,15 @@ NS_ASSUME_NONNULL_BEGIN
[self.tableView reloadData];
}
#pragma mark - Notifications
- (void)identityStateDidChange:(NSNotification *)notification
{
OWSAssert([NSThread isMainThread]);
[self updateTableContents];
}
#pragma mark - Logging
+ (NSString *)tag

View File

@ -63,6 +63,21 @@ NS_ASSUME_NONNULL_BEGIN
- (void)commonInit
{
_contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self];
[self observeNotifications];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(identityStateDidChange:)
name:kNSNotificationName_IdentityStateDidChange
object:nil];
}
- (void)configWithThread:(TSGroupThread *)thread
@ -134,6 +149,12 @@ NS_ASSUME_NONNULL_BEGIN
[cell configureWithRecipientId:recipientId contactsManager:helper.contactsManager];
}
BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId]
== OWSVerificationStateVerified;
if (isVerified) {
[cell addVerifiedSubtitle];
}
return cell;
}
customRowHeight:[ContactTableViewCell rowHeight]
@ -313,6 +334,15 @@ NS_ASSUME_NONNULL_BEGIN
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Notifications
- (void)identityStateDidChange:(NSNotification *)notification
{
OWSAssert([NSThread isMainThread]);
[self updateTableContents];
}
#pragma mark - Logging
+ (NSString *)tag

View File

@ -132,18 +132,18 @@ NSString *const SignalsViewControllerSegueShowIncomingCall = @"ShowIncomingCallS
- (void)blockedPhoneNumbersDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
[self.tableView reloadData];
});
OWSAssert([NSThread isMainThread]);
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
[self.tableView reloadData];
}
- (void)signalAccountsDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
OWSAssert([NSThread isMainThread]);
[self.tableView reloadData];
}
#pragma mark - View Life Cycle

View File

@ -61,9 +61,9 @@ NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey =
- (void)signalAccountsDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self sendSyncContactsMessageIfPossible];
});
OWSAssert([NSThread isMainThread]);
[self sendSyncContactsMessageIfPossible];
}
#pragma mark - Methods

View File

@ -33,6 +33,8 @@ extern NSString *const kContactsTable_CellReuseIdentifier;
- (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
- (void)addVerifiedSubtitle;
@end
NS_ASSUME_NONNULL_END

View File

@ -23,6 +23,7 @@ const NSUInteger kContactTableViewCellAvatarSize = 40;
@property (nonatomic) IBOutlet UILabel *nameLabel;
@property (nonatomic) IBOutlet UIImageView *avatarView;
@property (nonatomic, nullable) UILabel *subtitle;
@end
@ -144,11 +145,67 @@ const NSUInteger kContactTableViewCellAvatarSize = 40;
[self layoutSubviews];
}
- (void)addVerifiedSubtitle
{
[self.subtitle removeFromSuperview];
const CGFloat kSubtitlePointSize = 10.f;
NSMutableAttributedString *text = [NSMutableAttributedString new];
// "checkmark"
[text appendAttributedString:[[NSAttributedString alloc]
initWithString:@"\uf00c "
attributes:@{
NSFontAttributeName : [UIFont ows_fontAwesomeFont:kSubtitlePointSize],
}]];
[text appendAttributedString:[[NSAttributedString alloc]
initWithString:NSLocalizedString(@"PRIVACY_IDENTITY_IS_VERIFIED_BADGE",
@"Badge indicating that the user is verified.")]];
self.subtitle = [UILabel new];
self.subtitle.font = [UIFont ows_regularFontWithSize:kSubtitlePointSize];
self.subtitle.textColor = [UIColor ows_darkGrayColor];
self.subtitle.attributedText = text;
[self.subtitle sizeToFit];
[self.contentView addSubview:self.subtitle];
[self setNeedsLayout];
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
[self layoutSubviews];
}
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
[self layoutSubviews];
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.subtitle) {
OWSAssert(self.nameLabel.superview == self.contentView);
const CGFloat kSubtitleVMargin
= ((self.contentView.height - self.nameLabel.font.lineHeight) * 0.5f - self.subtitle.height) * 0.5f;
self.subtitle.frame = CGRectMake(self.nameLabel.left,
round((self.contentView.height - self.subtitle.height) - kSubtitleVMargin),
self.subtitle.width,
self.subtitle.height);
}
}
- (void)prepareForReuse
{
self.accessoryMessage = nil;
self.accessoryView = nil;
self.accessoryType = UITableViewCellAccessoryNone;
[self.subtitle removeFromSuperview];
self.subtitle = nil;
}
@end

View File

@ -715,9 +715,15 @@
/* message footer while attachment is uploading */
"MESSAGE_STATUS_UPLOADING" = "Uploading…";
/* Indicates that one member of this group conversation is no longer verified. Embeds {{user's name or phone number}}. */
"MESSAGES_VIEW_1_MEMBER_NO_LONGER_VERIFIED_FORMAT" = "%@ is no longer verified.";
/* Indicates that this 1:1 conversation has been blocked. */
"MESSAGES_VIEW_CONTACT_BLOCKED" = "You Blocked this User";
/* Indicates that this 1:1 conversation is no longer verified. Embeds {{user's name or phone number}}. */
"MESSAGES_VIEW_CONTACT_NO_LONGER_VERIFIED_FORMAT" = "%@ is no longer verified.";
/* Action sheet title after tapping on failed download. */
"MESSAGES_VIEW_FAILED_DOWNLOAD_ACTIONSHEET_TITLE" = "Download Failed.";
@ -730,6 +736,9 @@
/* Indicates that some members of this group has been blocked. Embeds {{the number of blocked users in this group}}. */
"MESSAGES_VIEW_GROUP_N_MEMBERS_BLOCKED_FORMAT" = "You Blocked %d Members of this Group";
/* Indicates that more than one member of this group conversation is no longer verified. */
"MESSAGES_VIEW_N_MEMBERS_NO_LONGER_VERIFIED" = "More than one member of this group is no longer verified.";
/* The subtitle for the messages view title indicates that the title can be tapped to access settings for this conversation. */
"MESSAGES_VIEW_TITLE_SUBTITLE" = "Tap here for settings";
@ -943,6 +952,9 @@
/* Label indicating that the user is not verified. Embeds {{the user's name or phone number}}. */
"PRIVACY_IDENTITY_IS_NOT_VERIFIED_FORMAT" = "%@ is not verified.";
/* Badge indicating that the user is verified. */
"PRIVACY_IDENTITY_IS_VERIFIED_BADGE" = "Verified";
/* Label indicating that the user is verified. Embeds {{the user's name or phone number}}. */
"PRIVACY_IDENTITY_IS_VERIFIED_FORMAT" = "%@ is verified.";