Rework contact share buttons.

pull/1/head
Matthew Chen 7 years ago
parent a0710febe4
commit 92332c2b69

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; };
3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; };
34074F61203D0CBE004596AE /* OWSSounds.m in Sources */ = {isa = PBXBuildFile; fileRef = 34074F5F203D0CBD004596AE /* OWSSounds.m */; };
34074F62203D0CBE004596AE /* OWSSounds.h in Headers */ = {isa = PBXBuildFile; fileRef = 34074F60203D0CBE004596AE /* OWSSounds.h */; settings = {ATTRIBUTES = (Public, ); }; };
340B02BA1FA0D6C700F9CFEC /* ConversationViewItemTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 340B02B91FA0D6C700F9CFEC /* ConversationViewItemTest.m */; };
@ -567,6 +568,8 @@
3400C7951EAF99F4008A8584 /* SelectThreadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SelectThreadViewController.m; sourceTree = "<group>"; };
3400C7971EAFB772008A8584 /* ThreadViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadViewHelper.h; sourceTree = "<group>"; };
3400C7981EAFB772008A8584 /* ThreadViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadViewHelper.m; sourceTree = "<group>"; };
3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = "<group>"; };
3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareButtonsView.h; sourceTree = "<group>"; };
34074F5F203D0CBD004596AE /* OWSSounds.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSounds.m; sourceTree = "<group>"; };
34074F60203D0CBE004596AE /* OWSSounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSounds.h; sourceTree = "<group>"; };
340B02B61F9FD31800F9CFEC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = translations/he.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -1750,6 +1753,8 @@
34DBF001206BD5A500025978 /* OWSBubbleView.m */,
34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */,
34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */,
3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */,
3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */,
34CA63192097806E00E526A0 /* OWSContactShareView.h */,
34CA631A2097806E00E526A0 /* OWSContactShareView.m */,
34D1F0B51F87F8850066283D /* OWSGenericAttachmentView.h */,
@ -3237,6 +3242,7 @@
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */,
45B27B862037FFB400A539DF /* DebugUIFileBrowser.swift in Sources */,
34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */,
3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */,
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */,
34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */,
340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */,

@ -0,0 +1,35 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class ContactShareViewModel;
@protocol OWSContactShareButtonsViewDelegate <NSObject>
- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare;
- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare;
- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare;
@end
#pragma mark -
@interface OWSContactShareButtonsView : UIView
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare
delegate:(id<OWSContactShareButtonsViewDelegate>)delegate;
- (void)createContents;
+ (CGFloat)bubbleHeight;
// Returns YES IFF the tap was handled.
- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender;
+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,161 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSContactShareButtonsView.h"
#import "Signal-Swift.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalMessaging/UIColor+OWS.h>
#import <SignalServiceKit/OWSContact.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSContactShareButtonsView ()
@property (nonatomic, readonly) ContactShareViewModel *contactShare;
@property (nonatomic, weak) id<OWSContactShareButtonsViewDelegate> delegate;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, nullable) UIView *buttonView;
@end
#pragma mark -
@implementation OWSContactShareButtonsView
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare
delegate:(id<OWSContactShareButtonsViewDelegate>)delegate
{
self = [super init];
if (self) {
_delegate = delegate;
_contactShare = contactShare;
_contactsManager = [Environment current].contactsManager;
}
return self;
}
#pragma mark -
+ (BOOL)hasSendTextButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
{
OWSAssert(contactShare);
OWSAssert(contactsManager);
return [contactShare systemContactsWithSignalAccountPhoneNumbers:contactsManager].count > 0;
}
+ (BOOL)hasInviteButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
{
OWSAssert(contactShare);
OWSAssert(contactsManager);
return [contactShare systemContactPhoneNumbers:contactsManager].count > 0;
}
+ (BOOL)hasAddToContactsButton:(ContactShareViewModel *)contactShare
{
OWSAssert(contactShare);
return [contactShare e164PhoneNumbers].count > 0;
}
+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare
{
OWSAssert(contactShare);
OWSContactsManager *contactsManager = [Environment current].contactsManager;
return [self hasAnyButton:contactShare contactsManager:contactsManager];
}
+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
{
OWSAssert(contactShare);
return ([self hasSendTextButton:contactShare contactsManager:contactsManager] ||
[self hasInviteButton:contactShare contactsManager:contactsManager] ||
[self hasAddToContactsButton:contactShare]);
}
+ (CGFloat)bubbleHeight
{
return self.buttonHeight;
}
+ (CGFloat)buttonHeight
{
return MAX(44.f, self.buttonFont.lineHeight + self.buttonVMargin * 2);
}
+ (UIFont *)buttonFont
{
return [UIFont ows_dynamicTypeBodyFont].ows_mediumWeight;
}
+ (CGFloat)buttonVMargin
{
return 5;
}
- (void)createContents
{
OWSAssert([OWSContactShareButtonsView hasAnyButton:self.contactShare contactsManager:self.contactsManager]);
self.layoutMargins = UIEdgeInsetsZero;
self.backgroundColor = [UIColor ows_light02Color];
UILabel *label = [UILabel new];
self.buttonView = label;
if ([OWSContactShareButtonsView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) {
label.text = NSLocalizedString(@"ACTION_SEND_MESSAGE", @"Label for 'sent message' button in contact view.");
} else if ([OWSContactShareButtonsView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) {
label.text = NSLocalizedString(@"ACTION_INVITE", @"Label for 'invite' button in contact view.");
} else if ([OWSContactShareButtonsView hasAddToContactsButton:self.contactShare]) {
label.text = NSLocalizedString(@"CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER",
@"Message shown in conversation view that offers to add an unknown user to your phone's contacts.");
} else {
OWSFail(@"%@ unexpected button state.", self.logTag);
}
label.font = OWSContactShareButtonsView.buttonFont;
label.textColor = UIColor.ows_materialBlueColor;
label.textAlignment = NSTextAlignmentCenter;
[self addSubview:label];
[label autoPinToSuperviewEdges];
[label autoSetDimension:ALDimensionHeight toSize:OWSContactShareButtonsView.buttonHeight];
}
- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender
{
if (!self.buttonView) {
return NO;
}
CGPoint location = [sender locationInView:self.buttonView];
if (!CGRectContainsPoint(self.buttonView.bounds, location)) {
return NO;
}
if ([OWSContactShareButtonsView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) {
[self.delegate didTapSendMessageToContactShare:self.contactShare];
} else if ([OWSContactShareButtonsView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) {
[self.delegate didTapSendInviteToContactShare:self.contactShare];
} else if ([OWSContactShareButtonsView hasAddToContactsButton:self.contactShare]) {
[self.delegate didTapShowAddToContactUIForContactShare:self.contactShare];
} else {
OWSFail(@"%@ unexpected button tap.", self.logTag);
}
return YES;
}
@end
NS_ASSUME_NONNULL_END

@ -5,31 +5,14 @@
NS_ASSUME_NONNULL_BEGIN
@class ContactShareViewModel;
@class OWSContact;
@class OWSContactsManager;
@protocol OWSContactShareViewDelegate <NSObject>
- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare;
- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare;
- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare;
@end
#pragma mark -
@interface OWSContactShareView : UIView
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare
isIncoming:(BOOL)isIncoming
delegate:(id<OWSContactShareViewDelegate>)delegate;
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare isIncoming:(BOOL)isIncoming;
- (void)createContents;
+ (CGFloat)bubbleHeightForContactShare:(ContactShareViewModel *)contactShare;
// Returns YES IFF the tap was handled.
- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender;
+ (CGFloat)bubbleHeight;
@end

@ -18,13 +18,10 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSContactShareView ()
@property (nonatomic, readonly) ContactShareViewModel *contactShare;
@property (nonatomic, weak) id<OWSContactShareViewDelegate> delegate;
@property (nonatomic, readonly) BOOL isIncoming;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, nullable) UIView *buttonView;
@end
#pragma mark -
@ -33,12 +30,10 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare
isIncoming:(BOOL)isIncoming
delegate:(id<OWSContactShareViewDelegate>)delegate
{
self = [super init];
if (self) {
_delegate = delegate;
_contactShare = contactShare;
_isIncoming = isIncoming;
_contactsManager = [Environment current].contactsManager;
@ -54,80 +49,29 @@ NS_ASSUME_NONNULL_BEGIN
return 12.f;
}
- (CGFloat)iconHSpacing
{
return 8.f;
}
+ (CGFloat)iconVMargin
{
return 12.f;
}
- (CGFloat)iconVMargin
{
return [OWSContactShareView iconVMargin];
}
+ (BOOL)hasSendTextButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
+ (CGFloat)vMargin
{
OWSAssert(contactShare);
OWSAssert(contactsManager);
return [contactShare systemContactsWithSignalAccountPhoneNumbers:contactsManager].count > 0;
return 0.f;
}
+ (BOOL)hasInviteButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
{
OWSAssert(contactShare);
OWSAssert(contactsManager);
return [contactShare systemContactPhoneNumbers:contactsManager].count > 0;
}
+ (BOOL)hasAddToContactsButton:(ContactShareViewModel *)contactShare
{
OWSAssert(contactShare);
return [contactShare e164PhoneNumbers].count > 0;
}
+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager
- (CGFloat)iconHSpacing
{
OWSAssert(contactShare);
return ([self hasSendTextButton:contactShare contactsManager:contactsManager] ||
[self hasInviteButton:contactShare contactsManager:contactsManager] ||
[self hasAddToContactsButton:contactShare]);
return 8.f;
}
+ (CGFloat)bubbleHeightForContactShare:(ContactShareViewModel *)contactShare
+ (CGFloat)bubbleHeight
{
OWSAssert(contactShare);
OWSContactsManager *contactsManager = [Environment current].contactsManager;
if ([self hasAnyButton:contactShare contactsManager:contactsManager]) {
return self.contentHeight + self.buttonHeight;
} else {
return self.contentHeight;
}
return self.contentHeight;
}
+ (CGFloat)contentHeight
{
CGFloat labelsHeight = (self.nameFont.lineHeight + self.labelsVSpacing + self.subtitleFont.lineHeight);
CGFloat contentHeight = MAX(self.iconSize, labelsHeight);
contentHeight += self.iconVMargin * 2;
contentHeight += OWSContactShareView.vMargin * 2;
return contentHeight;
}
+ (CGFloat)buttonHeight
{
return MAX(44.f, self.buttonFont.lineHeight + self.buttonVMargin * 2);
}
+ (CGFloat)iconSize
{
return 48.f;
@ -138,11 +82,6 @@ NS_ASSUME_NONNULL_BEGIN
return [OWSContactShareView iconSize];
}
- (CGFloat)vMargin
{
return 10.f;
}
+ (UIFont *)nameFont
{
return [UIFont ows_dynamicTypeBodyFont];
@ -158,16 +97,6 @@ NS_ASSUME_NONNULL_BEGIN
return 2;
}
+ (UIFont *)buttonFont
{
return [UIFont ows_dynamicTypeBodyFont];
}
+ (CGFloat)buttonVMargin
{
return 5;
}
- (void)createContents
{
self.layoutMargins = UIEdgeInsetsZero;
@ -220,61 +149,13 @@ NS_ASSUME_NONNULL_BEGIN
hStackView.spacing = self.iconHSpacing;
hStackView.alignment = UIStackViewAlignmentCenter;
hStackView.layoutMarginsRelativeArrangement = YES;
hStackView.layoutMargins = UIEdgeInsetsMake(self.vMargin, self.hMargin, self.vMargin, self.hMargin);
hStackView.layoutMargins
= UIEdgeInsetsMake(OWSContactShareView.vMargin, self.hMargin, OWSContactShareView.vMargin, self.hMargin);
[hStackView addArrangedSubview:avatarView];
[hStackView addArrangedSubview:labelsView];
[hStackView addArrangedSubview:disclosureImageView];
UIStackView *vStackView = [UIStackView new];
vStackView.axis = UILayoutConstraintAxisVertical;
vStackView.spacing = 0;
[self addSubview:vStackView];
[vStackView autoPinToSuperviewEdges];
[vStackView addArrangedSubview:hStackView];
if ([OWSContactShareView hasAnyButton:self.contactShare contactsManager:self.contactsManager]) {
UILabel *label = [UILabel new];
self.buttonView = label;
if ([OWSContactShareView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) {
label.text = NSLocalizedString(@"ACTION_SEND_MESSAGE", @"Label for 'sent message' button in contact view.");
} else if ([OWSContactShareView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) {
label.text = NSLocalizedString(@"ACTION_INVITE", @"Label for 'invite' button in contact view.");
} else if ([OWSContactShareView hasAddToContactsButton:self.contactShare]) {
label.text = NSLocalizedString(@"CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER",
@"Message shown in conversation view that offers to add an unknown user to your phone's contacts.");
} else {
OWSFail(@"%@ unexpected button state.", self.logTag);
}
label.font = OWSContactShareView.buttonFont;
label.textColor = UIColor.ows_materialBlueColor;
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor whiteColor];
[vStackView addArrangedSubview:label];
[label autoSetDimension:ALDimensionHeight toSize:OWSContactShareView.buttonHeight];
}
}
- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender
{
if (!self.buttonView) {
return NO;
}
CGPoint location = [sender locationInView:self.buttonView];
if (!CGRectContainsPoint(self.buttonView.bounds, location)) {
return NO;
}
if ([OWSContactShareView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) {
[self.delegate didTapSendMessageToContactShare:self.contactShare];
} else if ([OWSContactShareView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) {
[self.delegate didTapSendInviteToContactShare:self.contactShare];
} else if ([OWSContactShareView hasAddToContactsButton:self.contactShare]) {
[self.delegate didTapShowAddToContactUIForContactShare:self.contactShare];
} else {
OWSFail(@"%@ unexpected button tap.", self.logTag);
}
return YES;
[self addSubview:hStackView];
[hStackView autoPinToSuperviewEdges];
}
@end

@ -8,6 +8,7 @@
#import "OWSAudioMessageView.h"
#import "OWSBubbleShapeView.h"
#import "OWSBubbleView.h"
#import "OWSContactShareButtonsView.h"
#import "OWSContactShareView.h"
#import "OWSGenericAttachmentView.h"
#import "OWSMessageFooterView.h"
@ -19,19 +20,10 @@
NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageBubbleView () <OWSQuotedMessageViewDelegate, OWSContactShareViewDelegate>
@interface OWSMessageBubbleView () <OWSQuotedMessageViewDelegate, OWSContactShareButtonsViewDelegate>
@property (nonatomic) OWSBubbleView *bubbleView;
// TODO: We may only end up using a single shadow.
@property (nonatomic) OWSBubbleShapeView *mediaShadowView1;
@property (nonatomic) OWSBubbleShapeView *mediaShadowView2;
@property (nonatomic) OWSBubbleShapeView *mediaClipView;
@property (nonatomic) OWSBubbleShapeView *bubbleStrokeView;
@property (nonatomic) UIStackView *stackView;
@property (nonatomic) UILabel *senderNameLabel;
@ -52,8 +44,12 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) OWSMessageFooterView *footerView;
@property (nonatomic, nullable) OWSContactShareButtonsView *contactShareButtonsView;
@end
#pragma mark -
@implementation OWSMessageBubbleView
- (instancetype)initWithFrame:(CGRect)frame
@ -84,11 +80,6 @@ NS_ASSUME_NONNULL_BEGIN
[self addSubview:self.bubbleView];
[self.bubbleView autoPinEdgesToSuperviewEdges];
self.mediaShadowView1 = [OWSBubbleShapeView bubbleShadowView];
self.mediaShadowView2 = [OWSBubbleShapeView bubbleShadowView];
self.mediaClipView = [OWSBubbleShapeView bubbleClipView];
self.bubbleStrokeView = [OWSBubbleShapeView bubbleDrawView];
self.stackView = [UIStackView new];
self.stackView.axis = UILayoutConstraintAxisVertical;
@ -356,53 +347,64 @@ NS_ASSUME_NONNULL_BEGIN
}
if (self.hasBodyMediaWithThumbnail) {
// The "body media" view casts a shadow "downward" onto adjacent views,
// so we use a "proxy" view to take its place within the v-stack
// view and then insert the body media view above its proxy so that
// it floats above the other content of the bubble view.
UIView *bodyProxyView = [UIView new];
[self.stackView addArrangedSubview:bodyProxyView];
UIView *proxyView = [UIView new];
[self.stackView addArrangedSubview:proxyView];
// TODO: We may only end up using a single shadow.
OWSBubbleShapeView *shadowView1 = [OWSBubbleShapeView bubbleShadowView];
OWSBubbleShapeView *shadowView2 = [OWSBubbleShapeView bubbleShadowView];
OWSBubbleShapeView *clipView = [OWSBubbleShapeView bubbleClipView];
[self addSubview:self.mediaShadowView1];
[self addSubview:self.mediaShadowView2];
[self addSubview:self.mediaClipView];
[self addSubview:shadowView1];
[self addSubview:shadowView2];
[self addSubview:clipView];
[self.viewConstraints addObjectsFromArray:[self.mediaShadowView1 autoPinToEdgesOfView:bodyProxyView]];
[self.viewConstraints addObjectsFromArray:[self.mediaShadowView2 autoPinToEdgesOfView:bodyProxyView]];
[self.viewConstraints addObjectsFromArray:[self.mediaClipView autoPinToEdgesOfView:bodyProxyView]];
[self.viewConstraints addObjectsFromArray:[shadowView1 autoPinToEdgesOfView:proxyView]];
[self.viewConstraints addObjectsFromArray:[shadowView2 autoPinToEdgesOfView:proxyView]];
[self.viewConstraints addObjectsFromArray:[clipView autoPinToEdgesOfView:proxyView]];
[self.mediaClipView addSubview:bodyMediaView];
[clipView addSubview:bodyMediaView];
[self.viewConstraints addObjectsFromArray:[bodyMediaView autoPinToSuperviewEdges]];
[self.bubbleView addPartnerView:self.mediaShadowView1];
[self.bubbleView addPartnerView:self.mediaShadowView2];
[self.bubbleView addPartnerView:self.mediaClipView];
[self.bubbleView addPartnerView:shadowView1];
[self.bubbleView addPartnerView:shadowView2];
[self.bubbleView addPartnerView:clipView];
// TODO: Consider only using a single shadow for perf.
self.mediaShadowView1.fillColor = self.bubbleColor;
self.mediaShadowView1.layer.shadowColor = [UIColor blackColor].CGColor;
self.mediaShadowView1.layer.shadowOpacity = 0.2f;
self.mediaShadowView1.layer.shadowOffset = CGSizeMake(0.f, 4.f);
self.mediaShadowView1.layer.shadowRadius = 20.f;
self.mediaShadowView2.fillColor = self.bubbleColor;
self.mediaShadowView2.layer.shadowColor = [UIColor blackColor].CGColor;
self.mediaShadowView2.layer.shadowOpacity = 0.08f;
self.mediaShadowView2.layer.shadowOffset = CGSizeZero;
self.mediaShadowView2.layer.shadowRadius = 4.f;
shadowView1.fillColor = self.bubbleColor;
shadowView1.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView1.layer.shadowOpacity = 0.2f;
shadowView1.layer.shadowOffset = CGSizeMake(0.f, 4.f);
shadowView1.layer.shadowRadius = 20.f;
shadowView2.fillColor = self.bubbleColor;
shadowView2.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView2.layer.shadowOpacity = 0.08f;
shadowView2.layer.shadowOffset = CGSizeZero;
shadowView2.layer.shadowRadius = 4.f;
} else {
OWSAssert(self.cellType == OWSMessageCellType_ContactShare);
if (self.contactShareHasSpacerTop) {
UIView *spacerView = [UIView containerView];
[spacerView autoSetDimension:ALDimensionHeight toSize:self.contactShareVSpacing];
[spacerView setCompressionResistanceHigh];
[self.stackView addArrangedSubview:spacerView];
}
[self.stackView addArrangedSubview:bodyMediaView];
// TODO: Constants.
self.bubbleStrokeView.strokeColor = [UIColor lightGrayColor];
self.bubbleStrokeView.strokeThickness = 1.f;
[self.bubbleView addSubview:self.bubbleStrokeView];
[self.viewConstraints addObjectsFromArray:[self.bubbleStrokeView autoPinToSuperviewEdges]];
[self.bubbleView addPartnerView:self.bubbleStrokeView];
if (self.contactShareHasSpacerBottom) {
UIView *spacerView = [UIView containerView];
[spacerView autoSetDimension:ALDimensionHeight toSize:self.contactShareVSpacing];
[spacerView setCompressionResistanceHigh];
[self.stackView addArrangedSubview:spacerView];
}
}
} else {
[textViews addObject:bodyMediaView];
@ -483,15 +485,82 @@ NS_ASSUME_NONNULL_BEGIN
addObject:[bodyMediaView autoSetDimension:ALDimensionHeight toSize:bodyMediaSize.CGSizeValue.height]];
}
[self updateBubbleColor];
[self insertContactShareButtonsIfNecessary];
// If we're stroking the bubble edge, ensure the stroke
// view is in front of its peers to prevent it from being occluded.
[self.bubbleStrokeView.superview bringSubviewToFront:self.bubbleStrokeView];
[self updateBubbleColor];
[self configureBubbleRounding];
}
- (void)insertContactShareButtonsIfNecessary
{
if (self.cellType != OWSMessageCellType_ContactShare) {
return;
}
if (![OWSContactShareButtonsView hasAnyButton:self.viewItem.contactShare]) {
return;
}
OWSAssert(self.viewItem.contactShare);
OWSContactShareButtonsView *buttonsView =
[[OWSContactShareButtonsView alloc] initWithContactShare:self.viewItem.contactShare delegate:self];
[buttonsView createContents];
NSValue *_Nullable actionButtonsSize = [self actionButtonsSize];
OWSAssert(actionButtonsSize);
[self.viewConstraints addObjectsFromArray:@[
[buttonsView autoSetDimension:ALDimensionHeight toSize:actionButtonsSize.CGSizeValue.height],
]];
// The "body media" view casts a shadow "downward" onto adjacent views,
// so we use a "proxy" view to take its place within the v-stack
// view and then insert the body media view above its proxy so that
// it floats above the other content of the bubble view.
UIView *proxyView = [UIView new];
[self.stackView addArrangedSubview:proxyView];
// TODO: We may only end up using a single shadow.
OWSBubbleShapeView *shadowView = [OWSBubbleShapeView bubbleShadowView];
OWSBubbleShapeView *clipView = [OWSBubbleShapeView bubbleClipView];
[self addSubview:shadowView];
[self addSubview:clipView];
[self.viewConstraints addObjectsFromArray:[shadowView autoPinToEdgesOfView:proxyView]];
[self.viewConstraints addObjectsFromArray:[clipView autoPinToEdgesOfView:proxyView]];
[clipView addSubview:buttonsView];
[self.viewConstraints addObjectsFromArray:[buttonsView autoPinToSuperviewEdges]];
[self.bubbleView addPartnerView:shadowView];
[self.bubbleView addPartnerView:clipView];
OWSAssert(buttonsView.backgroundColor);
shadowView.fillColor = buttonsView.backgroundColor;
shadowView.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView.layer.shadowOpacity = 0.12f;
shadowView.layer.shadowOffset = CGSizeMake(0.f, 0.f);
shadowView.layer.shadowRadius = 1.f;
}
- (BOOL)contactShareHasSpacerTop
{
return (self.cellType == OWSMessageCellType_ContactShare && (self.isQuotedReply || !self.shouldShowSenderName));
}
- (BOOL)contactShareHasSpacerBottom
{
return (self.cellType == OWSMessageCellType_ContactShare && !self.hasBottomFooter);
}
- (CGFloat)contactShareVSpacing
{
return 12.f;
}
- (void)configureBubbleRounding
{
self.bubbleView.useSmallCorners_Top
@ -968,9 +1037,8 @@ NS_ASSUME_NONNULL_BEGIN
{
OWSAssert(self.viewItem.contactShare);
OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare
isIncoming:self.isIncoming
delegate:self];
OWSContactShareView *contactShareView =
[[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare isIncoming:self.isIncoming];
[contactShareView createContents];
// TODO: Should we change appearance if contact avatar is uploading?
@ -1115,8 +1183,7 @@ NS_ASSUME_NONNULL_BEGIN
case OWSMessageCellType_ContactShare:
OWSAssert(self.viewItem.contactShare);
result = CGSizeMake(
maxMessageWidth, [OWSContactShareView bubbleHeightForContactShare:self.viewItem.contactShare]);
result = CGSizeMake(maxMessageWidth, [OWSContactShareView bubbleHeight]);
break;
}
@ -1165,6 +1232,23 @@ NS_ASSUME_NONNULL_BEGIN
return [NSValue valueWithCGSize:result];
}
- (nullable NSValue *)actionButtonsSize
{
OWSAssert(self.conversationStyle);
OWSAssert(self.conversationStyle.maxMessageWidth > 0);
if (self.cellType == OWSMessageCellType_ContactShare) {
OWSAssert(self.viewItem.contactShare);
if ([OWSContactShareButtonsView hasAnyButton:self.viewItem.contactShare]) {
CGSize buttonsSize = CGSizeCeil(
CGSizeMake(self.conversationStyle.maxMessageWidth, [OWSContactShareButtonsView bubbleHeight]));
return [NSValue valueWithCGSize:buttonsSize];
}
}
return nil;
}
- (CGSize)measureSize
{
OWSAssert(self.conversationStyle);
@ -1201,6 +1285,13 @@ NS_ASSUME_NONNULL_BEGIN
[textViewSizes addObject:bodyMediaSize];
bodyMediaSize = nil;
}
if (self.contactShareHasSpacerTop) {
cellSize.height += self.contactShareVSpacing;
}
if (self.contactShareHasSpacerBottom) {
cellSize.height += self.contactShareVSpacing;
}
}
if (bodyMediaSize || quotedMessageSize) {
@ -1241,6 +1332,12 @@ NS_ASSUME_NONNULL_BEGIN
cellSize.height += self.tapForMoreHeight + self.textViewVSpacing;
}
NSValue *_Nullable actionButtonsSize = [self actionButtonsSize];
if (actionButtonsSize) {
cellSize.width = MAX(cellSize.width, actionButtonsSize.CGSizeValue.width);
cellSize.height += actionButtonsSize.CGSizeValue.height;
}
cellSize = CGSizeCeil(cellSize);
OWSAssert(cellSize.width <= self.conversationStyle.maxMessageWidth);
@ -1340,17 +1437,20 @@ NS_ASSUME_NONNULL_BEGIN
[self.quotedMessageView removeFromSuperview];
self.quotedMessageView = nil;
[self.mediaShadowView1 removeFromSuperview];
[self.mediaShadowView2 removeFromSuperview];
[self.mediaClipView removeFromSuperview];
[self.bubbleStrokeView removeFromSuperview];
[self.footerView removeFromSuperview];
[self.footerView prepareForReuse];
for (UIView *subview in self.stackView.subviews) {
[subview removeFromSuperview];
}
for (UIView *subview in self.subviews) {
if (subview != self.bubbleView) {
[subview removeFromSuperview];
}
}
[self.contactShareButtonsView removeFromSuperview];
self.contactShareButtonsView = nil;
}
#pragma mark - Gestures
@ -1381,9 +1481,8 @@ NS_ASSUME_NONNULL_BEGIN
}
}
if ([self.bodyMediaView isKindOfClass:[OWSContactShareView class]]) {
OWSContactShareView *contactShareView = (OWSContactShareView *)self.bodyMediaView;
if ([contactShareView handleTapGesture:sender]) {
if (self.contactShareButtonsView) {
if ([self.contactShareButtonsView handleTapGesture:sender]) {
return;
}
}
@ -1508,7 +1607,7 @@ NS_ASSUME_NONNULL_BEGIN
failedThumbnailDownloadAttachmentPointer:attachmentPointer];
}
#pragma mark - OWSContactShareViewDelegate
#pragma mark - OWSContactShareButtonsViewDelegate
- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare
{

@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
// NOTE: When changing the value of this feature flag, you also need
// to update the filtering in the SAE's info.plist.
BOOL kIsSendingContactSharesEnabled = NO;
BOOL kIsSendingContactSharesEnabled = YES;
NSString *NSStringForContactPhoneType(OWSContactPhoneType value)
{

Loading…
Cancel
Save