Sketch out rendering of contact shares in conversation view.

This commit is contained in:
Matthew Chen 2018-04-30 14:22:04 -04:00
parent 87abeb80be
commit 7e35a7e7d7
6 changed files with 277 additions and 3 deletions

View file

@ -188,6 +188,7 @@
34C6B0AB1FA0E46F00D35993 /* test-mp3.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0A71FA0E46F00D35993 /* test-mp3.mp3 */; };
34C6B0AC1FA0E46F00D35993 /* test-mp4.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0A81FA0E46F00D35993 /* test-mp4.mp4 */; };
34C6B0AE1FA0E4AA00D35993 /* test-jpg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */; };
34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CA631A2097806E00E526A0 /* OWSContactShareView.m */; };
34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; };
34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88E61F2FB9A10098030F /* ProfileViewController.m */; };
34CF0787203E6B78005C4D61 /* busy_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */; };
@ -802,6 +803,8 @@
34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "test-jpg.jpg"; sourceTree = "<group>"; };
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDetailViewController.swift; sourceTree = "<group>"; };
34CA1C281F7164F700E51C51 /* MediaMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaMessageView.swift; sourceTree = "<group>"; };
34CA63192097806E00E526A0 /* OWSContactShareView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareView.h; sourceTree = "<group>"; };
34CA631A2097806E00E526A0 /* OWSContactShareView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareView.m; sourceTree = "<group>"; };
34CCAF361F0C0599004084F4 /* AppUpdateNag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppUpdateNag.h; sourceTree = "<group>"; };
34CCAF371F0C0599004084F4 /* AppUpdateNag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppUpdateNag.m; sourceTree = "<group>"; };
34CE88E51F2FB9A10098030F /* ProfileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileViewController.h; sourceTree = "<group>"; };
@ -1711,6 +1714,8 @@
34DBF001206BD5A500025978 /* OWSBubbleView.m */,
34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */,
34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */,
34CA63192097806E00E526A0 /* OWSContactShareView.h */,
34CA631A2097806E00E526A0 /* OWSContactShareView.m */,
34D1F09C1F867BFC0066283D /* OWSExpirableMessageView.h */,
34D1F09D1F867BFC0066283D /* OWSExpirationTimerView.h */,
34D1F09E1F867BFC0066283D /* OWSExpirationTimerView.m */,
@ -3276,6 +3281,7 @@
34A910601FFEB114000C4745 /* OWSBackup.m in Sources */,
34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */,
45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */,
34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */,
34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */,
B90418E6183E9DD40038554A /* DateUtil.m in Sources */,
340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */,

View file

@ -0,0 +1,21 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class OWSContactShare;
@interface OWSContactShareView : UIView
- (instancetype)initWithContactShare:(OWSContactShare *)contactShare
contactShareName:(NSString *)contactShareName
isIncoming:(BOOL)isIncoming;
- (void)createContents;
+ (CGFloat)bubbleHeight;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,178 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSContactShareView.h"
#import "UIColor+JSQMessages.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import <SignalServiceKit/OWSContactShare.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSContactShareView ()
@property (nonatomic) OWSContactShare *contactShare;
@property (nonatomic) NSString *contactShareName;
@property (nonatomic) BOOL isIncoming;
@end
#pragma mark -
@implementation OWSContactShareView
- (instancetype)initWithContactShare:(OWSContactShare *)contactShare
contactShareName:(NSString *)contactShareName
isIncoming:(BOOL)isIncoming
{
self = [super init];
if (self) {
_contactShare = contactShare;
_contactShareName = contactShareName;
_isIncoming = isIncoming;
}
return self;
}
#pragma mark - JSQMessageMediaData protocol
- (CGFloat)iconHMargin
{
return 12.f;
}
- (CGFloat)iconHSpacing
{
return 10.f;
}
+ (CGFloat)iconVMargin
{
return 12.f;
}
- (CGFloat)iconVMargin
{
return [OWSContactShareView iconVMargin];
}
+ (CGFloat)bubbleHeight
{
return self.iconSize + self.iconVMargin * 2;
}
- (CGFloat)bubbleHeight
{
return [OWSContactShareView bubbleHeight];
}
+ (CGFloat)iconSize
{
return 44.f;
}
- (CGFloat)iconSize
{
return [OWSContactShareView iconSize];
}
- (CGFloat)vMargin
{
return 10.f;
}
- (UIColor *)bubbleBackgroundColor
{
return self.isIncoming ? [UIColor jsq_messageBubbleLightGrayColor] : [UIColor ows_materialBlueColor];
}
- (void)createContents
{
self.backgroundColor = [UIColor colorWithRGBHex:0xefeff4];
self.layoutMargins = UIEdgeInsetsZero;
// TODO: Verify that this layout works in RTL.
const CGFloat kBubbleTailWidth = 6.f;
UIView *contentView = [UIView containerView];
[self addSubview:contentView];
[contentView autoPinLeadingToSuperviewMarginWithInset:self.isIncoming ? kBubbleTailWidth : 0.f];
[contentView autoPinTrailingToSuperviewMarginWithInset:self.isIncoming ? 0.f : kBubbleTailWidth];
[contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin];
[contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin];
UIView *iconCircleView = [UIView containerView];
iconCircleView.backgroundColor = [UIColor colorWithRGBHex:0x00ffff];
iconCircleView.layer.cornerRadius = self.iconSize * 0.5f;
[iconCircleView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
[iconCircleView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
[iconCircleView setCompressionResistanceHigh];
[iconCircleView setContentHuggingHigh];
// TODO: Use avatar, if present and downloaded. else default.
UIImage *image = [UIImage imageNamed:@"attachment_file"];
OWSAssert(image);
UIImageView *imageView = [UIImageView new];
imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.tintColor = self.bubbleBackgroundColor;
[iconCircleView addSubview:imageView];
[imageView autoCenterInSuperview];
UILabel *topLabel = [UILabel new];
topLabel.text = self.contactShareName;
topLabel.textColor = [UIColor blackColor];
topLabel.lineBreakMode = NSLineBreakByTruncatingTail;
topLabel.font = [UIFont ows_dynamicTypeBodyFont];
UIStackView *labelsView = [UIStackView new];
labelsView.axis = UILayoutConstraintAxisVertical;
labelsView.spacing = 2;
[labelsView addArrangedSubview:topLabel];
// TODO: Should we just try to show the _first_ phone number?
// What about email?
// What if the second phone number is a signal account?
NSString *_Nullable firstPhoneNumber = self.contactShare.phoneNumbers.firstObject.phoneNumber;
if (firstPhoneNumber.length > 0) {
UILabel *bottomLabel = [UILabel new];
bottomLabel.text = firstPhoneNumber;
// TODO:
bottomLabel.textColor = [UIColor ows_darkGrayColor];
bottomLabel.lineBreakMode = NSLineBreakByTruncatingTail;
bottomLabel.font = [UIFont ows_dynamicTypeCaption1Font];
[labelsView addArrangedSubview:bottomLabel];
}
UIImage *disclosureImage =
[UIImage imageNamed:(self.isRTL ? @"system_disclosure_indicator_rtl" : @"system_disclosure_indicator")];
OWSAssert(disclosureImage);
UIImageView *disclosureImageView = [UIImageView new];
disclosureImageView.image = [disclosureImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
disclosureImageView.tintColor = [UIColor blackColor];
[disclosureImageView setCompressionResistanceHigh];
[disclosureImageView setContentHuggingHigh];
UIStackView *stackView = [UIStackView new];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.spacing = self.iconHSpacing;
stackView.alignment = UIStackViewAlignmentCenter;
[contentView addSubview:stackView];
[stackView autoPinLeadingToSuperviewMarginWithInset:self.iconHMargin];
[stackView autoPinTrailingToSuperviewMarginWithInset:self.iconHMargin];
[stackView autoVCenterInSuperview];
// NOTE: It's critical that we pin to the superview top and bottom _edge_ and not _margin_.
[stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView addArrangedSubview:iconCircleView];
[stackView addArrangedSubview:labelsView];
[stackView addArrangedSubview:disclosureImageView];
}
@end
NS_ASSUME_NONNULL_END

View file

@ -8,6 +8,7 @@
#import "OWSAudioMessageView.h"
#import "OWSBubbleStrokeView.h"
#import "OWSBubbleView.h"
#import "OWSContactShareView.h"
#import "OWSGenericAttachmentView.h"
#import "OWSMessageTextView.h"
#import "OWSQuotedMessageView.h"
@ -194,7 +195,7 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark -
- (BOOL)hasNonImageBodyContent
- (BOOL)hasBubbleBackground
{
switch (self.cellType) {
case OWSMessageCellType_Unknown:
@ -202,6 +203,7 @@ NS_ASSUME_NONNULL_BEGIN
case OWSMessageCellType_OversizeTextMessage:
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_ShareContact:
return YES;
case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage:
@ -226,6 +228,8 @@ NS_ASSUME_NONNULL_BEGIN
case OWSMessageCellType_Video:
// Is there a caption?
return self.hasBodyText;
case OWSMessageCellType_ShareContact:
return NO;
}
}
@ -245,7 +249,7 @@ NS_ASSUME_NONNULL_BEGIN
self.bubbleView.isOutgoing = self.isOutgoing;
self.bubbleView.hideTail = self.viewItem.shouldHideBubbleTail && !self.alwaysShowBubbleTail;
if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasNonImageBodyContent) {
if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasBubbleBackground) {
TSMessage *message = (TSMessage *)self.viewItem.interaction;
self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message];
} else {
@ -327,6 +331,11 @@ NS_ASSUME_NONNULL_BEGIN
bodyMediaView = [self loadViewForDownloadingAttachment];
bodyMediaViewHasGreedyWidth = YES;
break;
case OWSMessageCellType_ShareContact:
// TODO:
bodyMediaView = [self loadViewForShareContact];
bodyMediaViewHasGreedyWidth = YES;
break;
}
if (bodyMediaView) {
@ -784,6 +793,28 @@ NS_ASSUME_NONNULL_BEGIN
return customView;
}
- (UIView *)loadViewForShareContact
{
OWSAssert(self.viewItem.contactShare);
OWSAssert(self.viewItem.contactShareName.length > 0);
OWSContactShareView *contactShareView =
[[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare
contactShareName:self.viewItem.contactShareName
isIncoming:self.isIncoming];
[contactShareView createContents];
// [self addAttachmentUploadViewIfNecessary:attachmentView];
self.loadCellContentBlock = ^{
// Do nothing.
};
self.unloadCellContentBlock = ^{
// Do nothing.
};
return contactShareView;
}
- (void)addAttachmentUploadViewIfNecessary:(UIView *)attachmentView
{
[self addAttachmentUploadViewIfNecessary:attachmentView
@ -902,6 +933,8 @@ NS_ASSUME_NONNULL_BEGIN
return CGSizeMake(maxMessageWidth, [OWSGenericAttachmentView bubbleHeight]);
case OWSMessageCellType_DownloadingAttachment:
return CGSizeMake(200, 90);
case OWSMessageCellType_ShareContact:
return CGSizeMake(maxMessageWidth, [OWSContactShareView bubbleHeight]);
}
}
@ -1022,6 +1055,10 @@ NS_ASSUME_NONNULL_BEGIN
if (self.cellType == OWSMessageCellType_DownloadingAttachment) {
return NO;
}
if (self.cellType == OWSMessageCellType_ShareContact) {
// TODO: Handle this case.
return NO;
}
if (!self.attachmentStream) {
return NO;
}
@ -1168,6 +1205,9 @@ NS_ASSUME_NONNULL_BEGIN
}
break;
}
case OWSMessageCellType_ShareContact:
// TODO:
break;
}
}

View file

@ -17,6 +17,7 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
OWSMessageCellType_Video,
OWSMessageCellType_GenericAttachment,
OWSMessageCellType_DownloadingAttachment,
OWSMessageCellType_ShareContact,
};
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@ -26,6 +27,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@class ConversationViewCell;
@class DisplayableText;
@class OWSAudioMessageView;
@class OWSContactShare;
@class OWSQuotedReplyModel;
@class TSAttachmentPointer;
@class TSAttachmentStream;
@ -101,6 +103,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
// if a load has previously failed.
@property (nonatomic) BOOL didCellMediaFailToLoad;
- (nullable OWSContactShare *)contactShare;
- (nullable NSString *)contactShareName;
#pragma mark - UIMenuController
- (NSArray<UIMenuItem *> *)textMenuControllerItems;

View file

@ -11,6 +11,7 @@
#import "Signal-Swift.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <SignalMessaging/NSString+OWS.h>
#import <SignalServiceKit/OWSContactShare.h>
#import <SignalServiceKit/TSInteraction.h>
NS_ASSUME_NONNULL_BEGIN
@ -36,6 +37,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return @"OWSMessageCellType_DownloadingAttachment";
case OWSMessageCellType_Unknown:
return @"OWSMessageCellType_Unknown";
case OWSMessageCellType_ShareContact:
return @"OWSMessageCellType_ShareContact";
}
}
@ -62,6 +65,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
@property (nonatomic, readonly, nullable) NSString *quotedRecipientId;
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
@property (nonatomic, nullable) OWSContactShare *contactShare;
@property (nonatomic, nullable) NSString *contactShareName;
@property (nonatomic) CGSize mediaSize;
@end
@ -401,6 +406,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.hasViewState = YES;
TSMessage *message = (TSMessage *)self.interaction;
if (message.contactShare) {
// TODO: Format contact share name.
NSString *contactShareName = @"Alice";
if (contactShareName.length > 0) {
self.contactShare = message.contactShare;
self.contactShareName = contactShareName;
self.messageCellType = OWSMessageCellType_ShareContact;
return;
}
}
TSAttachment *_Nullable attachment = [self firstAttachmentIfAnyOfMessage:message transaction:transaction];
if (attachment) {
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
@ -712,6 +727,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSFail(@"%@ No text to copy", self.logTag);
break;
}
case OWSMessageCellType_ShareContact: {
// TODO: Implement copy contact.
OWSFail(@"%@ Not implemented yet", self.logTag);
break;
}
}
}
@ -720,7 +740,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage: {
case OWSMessageCellType_OversizeTextMessage:
case OWSMessageCellType_ShareContact: {
OWSFail(@"%@ No media to copy", self.logTag);
break;
}
@ -802,6 +823,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage:
case OWSMessageCellType_ShareContact:
return NO;
case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage:
@ -824,6 +846,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage:
case OWSMessageCellType_ShareContact:
OWSFail(@"%@ Cannot save text data.", self.logTag);
break;
case OWSMessageCellType_StillImage:
@ -879,6 +902,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage:
case OWSMessageCellType_ShareContact:
return NO;
case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage: