mirror of https://github.com/oxen-io/session-ios
Unfork JSQMessagesViewController
Geting back on upstream fixes a couple bugs (see ##Bugfixes), and also will make future updates easier. The unforking process was basically this: * move custom message types (Calls and DisplayedMessages) classes from our custom JSQMVC fork into Signal-iOS. * Move any method customization into our subclass. Including ColletionView stuff, bubble sizing, and gesture behavior Bug Fixes --------- * Fix mis-sized incoming media bubbles. Bubble size was being cached by interaction id. Which broke when receiving an attachment. The problem is that incoming media messages were initially the height of a "Downloading Attachment" info message. Instead we use the mediaHash for media messages to expire the bubble size when the media changes. * fix missized bubble when MVC did appear The MessagesViewController isn't sized correctly until ViewWillAppear. This caused the first round of bubbles to be rendered incorrectly (they assumed a larger container than they had). I think is reflected in the current version of the app by a reflow occurring shortly after the view appears. Chores ------ * bump travis to build with xcode8 * specify RQV development team for device build. required by xcode 8 beta Cleanup ------ * Refactor messageing XIB so that elements are hangning outside of the views frame * Fix compiler warning with explicit cast * delete deprecated lineBreakmode, it's the default value anyway. // FREEBIEpull/1/head
parent
987ce5f832
commit
4d320d6015
@ -0,0 +1,76 @@
|
||||
//
|
||||
// JSQCall.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 20/11/14.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "JSQMessageData.h"
|
||||
#import "TSMessageAdapter.h"
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
kCallOutgoing = 1,
|
||||
kCallIncoming = 2,
|
||||
kCallMissed = 3,
|
||||
kGroupUpdateJoin = 4,
|
||||
kGroupUpdateLeft = 5,
|
||||
kGroupUpdate = 6
|
||||
} CallStatus;
|
||||
|
||||
|
||||
@interface JSQCall : NSObject <JSQMessageData, NSCoding, NSCopying>
|
||||
|
||||
/*
|
||||
* Returns the string Id of the user who initiated the call
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *senderId;
|
||||
|
||||
|
||||
/*
|
||||
* Returns the display name for user who initiated the call
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *senderDisplayName;
|
||||
|
||||
/*
|
||||
* Returns date of the call
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSDate *date;
|
||||
|
||||
/*
|
||||
* Returns the call status
|
||||
* @see CallStatus
|
||||
*/
|
||||
@property (nonatomic) CallStatus status;
|
||||
|
||||
/*
|
||||
* Returns message type for adapter
|
||||
*/
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
|
||||
/*
|
||||
* User can configure whether a thumbnail is used in the display of this cell or not
|
||||
*/
|
||||
@property (nonatomic) BOOL useThumbnail;
|
||||
|
||||
/**
|
||||
* String to be displayed
|
||||
*/
|
||||
|
||||
@property (nonatomic, copy) NSString *detailString;
|
||||
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (instancetype)initWithCallerId:(NSString *)callerId
|
||||
callerDisplayName:(NSString *)callerDisplayName
|
||||
date:(NSDate *)date
|
||||
status:(CallStatus)status
|
||||
displayString:(NSString*)detailString;
|
||||
|
||||
-(NSString*)dateText;
|
||||
|
||||
-(UIImage*)thumbnailImage;
|
||||
|
||||
@end
|
@ -0,0 +1,170 @@
|
||||
//
|
||||
// JSQCall.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 20/11/14.
|
||||
//
|
||||
|
||||
#import "JSQCall.h"
|
||||
|
||||
#import "JSQMessagesTimestampFormatter.h"
|
||||
#import "UIImage+JSQMessages.h"
|
||||
|
||||
@implementation JSQCall
|
||||
|
||||
#pragma mark - Initialzation
|
||||
|
||||
-(instancetype)initWithCallerId:(NSString *)senderId
|
||||
callerDisplayName:(NSString *)senderDisplayName
|
||||
date:(NSDate *)date
|
||||
status:(CallStatus)status
|
||||
displayString:(NSString *)detailString
|
||||
{
|
||||
NSParameterAssert(senderId != nil);
|
||||
NSParameterAssert(senderDisplayName != nil);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_senderId = [senderId copy];
|
||||
_senderDisplayName = [senderDisplayName copy];
|
||||
_date = [date copy];
|
||||
_status = status;
|
||||
_messageType = TSCallAdapter;
|
||||
_detailString = [detailString stringByAppendingFormat:@" "];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id)init
|
||||
{
|
||||
NSAssert(NO,@"%s is not a valid initializer for %@. Use %@ instead", __PRETTY_FUNCTION__, [self class], NSStringFromSelector(@selector(initWithCallerId:callerDisplayName:date:status:displayString:)));
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
_senderId = nil;
|
||||
_senderDisplayName = nil;
|
||||
_date = nil;
|
||||
}
|
||||
|
||||
-(NSString*)dateText
|
||||
{
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.timeStyle = NSDateFormatterShortStyle;
|
||||
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
|
||||
dateFormatter.doesRelativeDateFormatting = YES;
|
||||
return [dateFormatter stringFromDate:_date];
|
||||
}
|
||||
|
||||
-(UIImage*)thumbnailImage {
|
||||
// This relies on those assets being in the project
|
||||
if(!_useThumbnail) {
|
||||
return nil;
|
||||
}
|
||||
switch (_status) {
|
||||
case kCallOutgoing:
|
||||
return [UIImage imageNamed:@"statCallOutgoing--blue"];
|
||||
break;
|
||||
case kCallIncoming:
|
||||
case kCallMissed:
|
||||
return [UIImage imageNamed:@"statCallIncoming--blue"];
|
||||
break;
|
||||
case kGroupUpdate:
|
||||
return [UIImage imageNamed:@"statRefreshedGroup--blue"];
|
||||
break;
|
||||
case kGroupUpdateLeft:
|
||||
return [UIImage imageNamed:@"statLeftGroup--blue"];
|
||||
break;
|
||||
case kGroupUpdateJoin:
|
||||
return [UIImage imageNamed:@"statJoinedGroup--blue"];
|
||||
break;
|
||||
default:
|
||||
return nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
-(BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self==object) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (![object isKindOfClass:[self class]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
JSQCall * aCall = (JSQCall*)object;
|
||||
|
||||
return [self.senderId isEqualToString:aCall.senderId]
|
||||
&& [self.senderDisplayName isEqualToString:aCall.senderDisplayName]
|
||||
&& ([self.date compare:aCall.date] == NSOrderedSame)
|
||||
&& self.status == aCall.status;
|
||||
}
|
||||
|
||||
-(NSUInteger)hash
|
||||
{
|
||||
return self.senderId.hash ^ self.date.hash;
|
||||
}
|
||||
|
||||
-(NSString*)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@>",
|
||||
[self class], self.senderId, self.senderDisplayName, self.date];
|
||||
}
|
||||
|
||||
#pragma mark - JSQMessageData
|
||||
|
||||
//TODO I'm not sure this is right. It affects bubble rendering.
|
||||
- (BOOL)isMediaMessage {
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
-(instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_senderId = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderId))];
|
||||
_senderDisplayName = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderDisplayName))];
|
||||
_date = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(date))];
|
||||
_status = (CallStatus)[aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(status))];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:self.senderId forKey:NSStringFromSelector(@selector(senderId))];
|
||||
[aCoder encodeObject:self.senderDisplayName forKey:NSStringFromSelector(@selector(senderDisplayName))];
|
||||
[aCoder encodeObject:self.date forKey:NSStringFromSelector(@selector(date))];
|
||||
[aCoder encodeDouble:self.status forKey:NSStringFromSelector(@selector(status))];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
-(instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return [[[self class] allocWithZone:zone]initWithCallerId:self.senderId
|
||||
callerDisplayName:self.senderDisplayName
|
||||
date:self.date
|
||||
status:self.status
|
||||
displayString:self.detailString];
|
||||
}
|
||||
|
||||
- (NSUInteger)messageHash{
|
||||
return self.hash;
|
||||
}
|
||||
|
||||
- (NSString *)text{
|
||||
return _detailString;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,45 @@
|
||||
//
|
||||
// JSQDisplayedMessage.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "JSQMessageData.h"
|
||||
#import "TSMessageAdapter.h"
|
||||
|
||||
/* JSQDisplayed message is the parent class for displaying information to the user
|
||||
* from within the conversation view. Do not use directly :
|
||||
*
|
||||
* @see JSQInfoMessage
|
||||
* @see JSQErrorMessage
|
||||
*
|
||||
*/
|
||||
|
||||
@interface JSQDisplayedMessage : NSObject <JSQMessageData>
|
||||
|
||||
/*
|
||||
* Returns the unique identifier of the person affected by the displayed message
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *senderId;
|
||||
|
||||
|
||||
/*
|
||||
* Returns the name of the person affected by the displayed message
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSString *senderDisplayName;
|
||||
|
||||
/*
|
||||
* Returns date of the displayed message
|
||||
*/
|
||||
@property (copy, nonatomic, readonly) NSDate *date;
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
-(instancetype)initWithSenderId:(NSString*)senderId
|
||||
senderDisplayName:(NSString*)senderDisplayName
|
||||
date:(NSDate*)date;
|
||||
|
||||
@end
|
@ -0,0 +1,44 @@
|
||||
//
|
||||
// JSQDisplayedMessage.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQDisplayedMessage.h"
|
||||
|
||||
@implementation JSQDisplayedMessage
|
||||
|
||||
-(id)init
|
||||
{
|
||||
NSAssert(NO,@"%s is not a valid initializer for %@. Use %@ instead", __PRETTY_FUNCTION__, [self class], NSStringFromSelector(@selector(initWithSenderId:senderDisplayName:date:)));
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(instancetype)initWithSenderId:(NSString*)senderId
|
||||
senderDisplayName:(NSString*)senderDisplayName
|
||||
date:(NSDate*)date
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_senderId = [senderId copy];
|
||||
_senderDisplayName = [senderDisplayName copy];
|
||||
_date = [date copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)messageHash
|
||||
{
|
||||
return self.date.hash ^ self.senderId.hash;
|
||||
}
|
||||
|
||||
- (BOOL)isMediaMessage
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,36 @@
|
||||
//
|
||||
// JSQErrorMessage.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQDisplayedMessage.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, JSQErrorMessageType){
|
||||
JSQErrorMessageNoSession,
|
||||
JSQErrorMessageWrongTrustedIdentityKey,
|
||||
JSQErrorMessageInvalidKeyException,
|
||||
JSQErrorMessageMissingKeyId,
|
||||
JSQErrorMessageInvalidMessage,
|
||||
JSQErrorMessageDuplicateMessage,
|
||||
JSQErrorMessageInvalidVersion
|
||||
};
|
||||
|
||||
@interface JSQErrorMessage : JSQDisplayedMessage
|
||||
|
||||
@property (nonatomic) JSQErrorMessageType errorMessageType;
|
||||
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (instancetype)initWithErrorType:(JSQErrorMessageType)messageType
|
||||
senderId:(NSString*)senderId
|
||||
senderDisplayName:(NSString*)senderDisplayName
|
||||
date:(NSDate*)date;
|
||||
|
||||
- (NSString*)text;
|
||||
|
||||
@end
|
@ -0,0 +1,75 @@
|
||||
//
|
||||
// JSQErrorMessage.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQErrorMessage.h"
|
||||
|
||||
@implementation JSQErrorMessage
|
||||
|
||||
- (instancetype)initWithErrorType:(JSQErrorMessageType)messageType
|
||||
senderId:(NSString *)senderId
|
||||
senderDisplayName:(NSString *)senderDisplayName
|
||||
date:(NSDate *)date
|
||||
{
|
||||
self = [super initWithSenderId:senderId senderDisplayName:senderDisplayName date:date];
|
||||
|
||||
if (self) {
|
||||
_errorMessageType = messageType;
|
||||
_messageType = TSErrorMessageAdapter;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)text
|
||||
{
|
||||
switch (self.errorMessageType) {
|
||||
case JSQErrorMessageNoSession:
|
||||
return [NSString stringWithFormat:@"No session error"];
|
||||
break;
|
||||
case JSQErrorMessageWrongTrustedIdentityKey:
|
||||
return [NSString stringWithFormat:@"Error : Wrong trusted identity key for %@.", self.senderDisplayName];
|
||||
break;
|
||||
case JSQErrorMessageInvalidKeyException:
|
||||
return [NSString stringWithFormat:@"Error : Invalid key exception for %@.", self.senderDisplayName];
|
||||
break;
|
||||
case JSQErrorMessageMissingKeyId:
|
||||
return [NSString stringWithFormat:@"Error: Missing key identifier for %@", self.senderDisplayName];
|
||||
break;
|
||||
case JSQErrorMessageInvalidMessage:
|
||||
return [NSString stringWithFormat:@"Error: Invalid message"];
|
||||
break;
|
||||
case JSQErrorMessageDuplicateMessage:
|
||||
return [NSString stringWithFormat:@"Error: Duplicate message"];
|
||||
break;
|
||||
case JSQErrorMessageInvalidVersion:
|
||||
return [NSString stringWithFormat:@"Error: Invalid version for contact %@.", self.senderDisplayName];
|
||||
break;
|
||||
|
||||
default:
|
||||
return nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return self.senderId.hash ^ self.date.hash;
|
||||
}
|
||||
|
||||
- (NSString*)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@, type=%ld>",
|
||||
[self class], self.senderId, self.senderDisplayName, self.date, self.errorMessageType];
|
||||
}
|
||||
|
||||
-(TSMessageAdapterType)messageType
|
||||
{
|
||||
return TSErrorMessageAdapter;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,31 @@
|
||||
//
|
||||
// JSQInfoMessage.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQDisplayedMessage.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, JSQInfoMessageType){
|
||||
JSQInfoMessageTypeSessionDidEnd,
|
||||
};
|
||||
|
||||
@interface JSQInfoMessage : JSQDisplayedMessage
|
||||
|
||||
@property (nonatomic) JSQInfoMessageType infoMessageType;
|
||||
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (instancetype)initWithInfoType:(JSQInfoMessageType)messageType
|
||||
senderId:(NSString*)senderId
|
||||
senderDisplayName:(NSString*)senderDisplayName
|
||||
date:(NSDate*)date;
|
||||
|
||||
- (NSString*)text;
|
||||
|
||||
|
||||
@end
|
@ -0,0 +1,54 @@
|
||||
//
|
||||
// JSQInfoMessage.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQInfoMessage.h"
|
||||
|
||||
@implementation JSQInfoMessage
|
||||
|
||||
- (instancetype)initWithInfoType:(JSQInfoMessageType)messageType
|
||||
senderId:(NSString *)senderId
|
||||
senderDisplayName:(NSString *)senderDisplayName
|
||||
date:(NSDate *)date
|
||||
{
|
||||
//@discussion: NSParameterAssert() ?
|
||||
|
||||
self = [super initWithSenderId:senderId senderDisplayName:senderDisplayName date:date];
|
||||
|
||||
if (self) {
|
||||
_infoMessageType = messageType;
|
||||
_messageType = TSInfoMessageAdapter;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSString*)text
|
||||
{
|
||||
switch (self.infoMessageType) {
|
||||
case JSQInfoMessageTypeSessionDidEnd:
|
||||
return [NSString stringWithFormat:@"Session with %@ ended.", self.senderDisplayName];
|
||||
break;
|
||||
|
||||
default:
|
||||
return nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSUInteger)hash
|
||||
{
|
||||
return self.senderId.hash ^ self.date.hash;
|
||||
}
|
||||
|
||||
-(NSString*)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@, type=%ld>",
|
||||
[self class], self.senderId, self.senderDisplayName, self.date, self.infoMessageType];
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,13 @@
|
||||
//
|
||||
// OWSMessagesBubblesSizeCalculator.h
|
||||
// Signal
|
||||
//
|
||||
// Created by Michael Kirk on 7/10/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <JSQMessagesViewController/JSQMessagesBubblesSizeCalculator.h>
|
||||
|
||||
@interface OWSMessagesBubblesSizeCalculator : JSQMessagesBubblesSizeCalculator
|
||||
|
||||
@end
|
@ -0,0 +1,45 @@
|
||||
//
|
||||
// OWSMessagesBubblesSizeCalculator.m
|
||||
// Signal
|
||||
//
|
||||
// Created by Michael Kirk on 7/10/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSMessagesBubblesSizeCalculator.h"
|
||||
#import "TSMessageAdapter.h"
|
||||
#import "JSQDisplayedMessageCollectionViewCell.h"
|
||||
|
||||
@implementation OWSMessagesBubblesSizeCalculator
|
||||
|
||||
/**
|
||||
* Computes and returns the size of the `messageBubbleImageView` property
|
||||
* of a `JSQMessagesCollectionViewCell` for the specified messageData at indexPath.
|
||||
*
|
||||
* @param messageData A message data object.
|
||||
* @param indexPath The index path at which messageData is located.
|
||||
* @param layout The layout object asking for this information.
|
||||
*
|
||||
* @return A sizes that specifies the required dimensions to display the entire message contents.
|
||||
* Note, this is *not* the entire cell, but only its message bubble.
|
||||
*/
|
||||
- (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
|
||||
atIndexPath:(NSIndexPath *)indexPath
|
||||
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
|
||||
{
|
||||
CGSize superSize = [super messageBubbleSizeForMessageData:messageData
|
||||
atIndexPath:indexPath
|
||||
withLayout:layout];
|
||||
|
||||
TSMessageAdapter *message = (TSMessageAdapter *)messageData;
|
||||
if (message.messageType == TSInfoMessageAdapter ||
|
||||
message.messageType == TSErrorMessageAdapter) {
|
||||
|
||||
// Prevent cropping message text by accounting for message container/icon
|
||||
superSize.height = OWSDisplayedMessageCellHeight;
|
||||
}
|
||||
|
||||
return superSize;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,31 @@
|
||||
//
|
||||
// JSQCallCollectionViewCell.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 20/11/14.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <JSQMessagesViewController/JSQMessagesCollectionViewCell.h>
|
||||
|
||||
#define kCallCellHeight 40.0f
|
||||
#define kCallCellWidth 400.0f
|
||||
|
||||
@interface JSQCallCollectionViewCell : JSQMessagesCollectionViewCell
|
||||
|
||||
//TODO can we use an existing label from JSQMessagesCollectionViewCell?
|
||||
@property (weak, nonatomic, readonly) JSQMessagesLabel *cellLabel;
|
||||
|
||||
@property (weak, nonatomic, readonly) UIImageView *outgoingCallImageView;
|
||||
|
||||
@property (weak, nonatomic, readonly) UIImageView *incomingCallImageView;
|
||||
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
+ (UINib *)nib;
|
||||
|
||||
+ (NSString *)cellReuseIdentifier;
|
||||
|
||||
@end
|
@ -0,0 +1,63 @@
|
||||
//
|
||||
// JSQCallCollectionViewCell.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 20/11/14.
|
||||
//
|
||||
|
||||
#import "JSQCallCollectionViewCell.h"
|
||||
|
||||
#import "UIView+JSQMessages.h"
|
||||
|
||||
|
||||
@interface JSQCallCollectionViewCell ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *outgoingCallImageView;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *incomingCallImageView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JSQCallCollectionViewCell
|
||||
|
||||
#pragma mark - Class Methods
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.cellLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.cellLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f];
|
||||
self.cellLabel.textColor = [UIColor lightGrayColor];
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
_cellLabel = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Collection view cell
|
||||
|
||||
-(void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
self.cellLabel.text = nil;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="Efo-Hk-7Hw" customClass="JSQCallCollectionViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YEr-eC-P6i" customClass="JSQMessagesLabel">
|
||||
<rect key="frame" x="39" y="0.0" width="242" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="7nw-w2-91p"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="o2l-Ms-1mk">
|
||||
<rect key="frame" x="281" y="0.0" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="78L-mQ-gEo"/>
|
||||
<constraint firstAttribute="width" constant="20" id="olH-5o-XyR"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="H8m-r4-eEC">
|
||||
<rect key="frame" x="19" y="0.0" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="Qay-jM-aBk"/>
|
||||
<constraint firstAttribute="width" constant="20" id="RpE-jJ-cYX"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstItem="YEr-eC-P6i" firstAttribute="centerY" secondItem="o2l-Ms-1mk" secondAttribute="centerY" id="8wg-Tg-9Nh"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YEr-eC-P6i" secondAttribute="trailing" constant="39" id="Hj7-z5-WMM"/>
|
||||
<constraint firstItem="YEr-eC-P6i" firstAttribute="leading" secondItem="H8m-r4-eEC" secondAttribute="trailing" id="ZZJ-jL-10d"/>
|
||||
<constraint firstItem="YEr-eC-P6i" firstAttribute="top" secondItem="Efo-Hk-7Hw" secondAttribute="top" id="rEI-cY-6lx"/>
|
||||
<constraint firstItem="YEr-eC-P6i" firstAttribute="leading" secondItem="Efo-Hk-7Hw" secondAttribute="leading" constant="39" id="uNd-aK-1hE"/>
|
||||
<constraint firstItem="YEr-eC-P6i" firstAttribute="centerY" secondItem="H8m-r4-eEC" secondAttribute="centerY" id="wg8-V9-pBf"/>
|
||||
<constraint firstItem="o2l-Ms-1mk" firstAttribute="leading" secondItem="YEr-eC-P6i" secondAttribute="trailing" id="wmR-MD-QeR"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="320" height="20"/>
|
||||
<connections>
|
||||
<outlet property="cellLabel" destination="YEr-eC-P6i" id="jii-8O-zLL"/>
|
||||
<outlet property="incomingCallImageView" destination="H8m-r4-eEC" id="hVW-Ng-BnU"/>
|
||||
<outlet property="outgoingCallImageView" destination="o2l-Ms-1mk" id="Q5m-uX-80H"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="219" y="435"/>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
</document>
|
@ -0,0 +1,21 @@
|
||||
//
|
||||
// JSQDisplayedMessageCollectionViewCell.h
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <JSQMessagesViewController/JSQMessagesCollectionViewCell.h>
|
||||
|
||||
static const CGFloat OWSDisplayedMessageCellHeight = 70.0f;
|
||||
|
||||
@interface JSQDisplayedMessageCollectionViewCell : JSQMessagesCollectionViewCell
|
||||
|
||||
// TODO can we use existing label from superclass?
|
||||
@property (weak, nonatomic, readonly) JSQMessagesLabel * cellLabel;
|
||||
@property (weak, nonatomic, readonly) UIImageView * headerImageView;
|
||||
@property (strong, nonatomic, readonly) UIView *textContainer;
|
||||
|
||||
@end
|
@ -0,0 +1,63 @@
|
||||
//
|
||||
// JSQDisplayedMessageCollectionViewCell.m
|
||||
// JSQMessages
|
||||
//
|
||||
// Created by Dylan Bourgeois on 29/11/14.
|
||||
// Copyright (c) 2014 Hexed Bits. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JSQDisplayedMessageCollectionViewCell.h"
|
||||
|
||||
#import <JSQMessagesViewController/UIView+JSQMessages.h>
|
||||
|
||||
@interface JSQDisplayedMessageCollectionViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet JSQMessagesLabel* cellLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView* headerImageView;
|
||||
@property (strong, nonatomic) IBOutlet UIView *textContainer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JSQDisplayedMessageCollectionViewCell
|
||||
|
||||
#pragma mark - Class Methods
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
// self.cellLabelHeightConstraint.constant = 0.0f;
|
||||
|
||||
self.textContainer.layer.borderColor = [[UIColor lightGrayColor] CGColor];
|
||||
self.textContainer.layer.borderWidth = 0.75f;
|
||||
self.textContainer.layer.cornerRadius = 5.0f;
|
||||
self.cellLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.cellLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f];
|
||||
self.cellLabel.textColor = [UIColor lightGrayColor];
|
||||
}
|
||||
|
||||
#pragma mark - Collection view cell
|
||||
|
||||
-(void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
|
||||
self.cellLabel.text = nil;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="eMU-z2-CzM" customClass="JSQDisplayedMessageCollectionViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="70"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="70"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qCf-bs-dBd" userLabel="textContainer">
|
||||
<rect key="frame" x="0.0" y="22" width="320" height="48"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
|
||||
<rect key="frame" x="8" y="12" width="304" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="14" id="fed-2c-dqd"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="OVa-Xw-5vl" firstAttribute="leading" secondItem="qCf-bs-dBd" secondAttribute="leading" constant="8" id="2IE-8k-czI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OVa-Xw-5vl" secondAttribute="bottom" constant="8" id="MtI-jW-t1x"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OVa-Xw-5vl" secondAttribute="trailing" constant="8" id="Y8z-8G-PLt"/>
|
||||
<constraint firstItem="OVa-Xw-5vl" firstAttribute="top" secondItem="qCf-bs-dBd" secondAttribute="top" constant="12" id="v5B-tB-pOB"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="warning_white.png" translatesAutoresizingMaskIntoConstraints="NO" id="ePO-Cy-jUE">
|
||||
<rect key="frame" x="143" y="0.0" width="35" height="35"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="35" id="Llx-81-oyV"/>
|
||||
<constraint firstAttribute="width" constant="35" id="Nth-3D-Wo9"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstItem="ePO-Cy-jUE" firstAttribute="top" secondItem="eMU-z2-CzM" secondAttribute="top" id="D28-BQ-Qam"/>
|
||||
<constraint firstAttribute="trailing" secondItem="qCf-bs-dBd" secondAttribute="trailing" id="F3T-1l-nCg"/>
|
||||
<constraint firstItem="qCf-bs-dBd" firstAttribute="leading" secondItem="eMU-z2-CzM" secondAttribute="leading" id="OzF-VM-85V"/>
|
||||
<constraint firstAttribute="bottom" secondItem="qCf-bs-dBd" secondAttribute="bottom" id="PNq-zm-usq"/>
|
||||
<constraint firstItem="qCf-bs-dBd" firstAttribute="top" secondItem="ePO-Cy-jUE" secondAttribute="centerY" constant="4" id="UzU-DS-8WZ"/>
|
||||
<constraint firstItem="ePO-Cy-jUE" firstAttribute="centerX" secondItem="eMU-z2-CzM" secondAttribute="centerX" id="qtQ-mS-o6z"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="320" height="55"/>
|
||||
<connections>
|
||||
<outlet property="cellLabel" destination="OVa-Xw-5vl" id="7PC-oj-dQZ"/>
|
||||
<outlet property="headerImageView" destination="ePO-Cy-jUE" id="4uq-2C-V7U"/>
|
||||
<outlet property="textContainer" destination="qCf-bs-dBd" id="fL7-LO-El1"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="219" y="433"/>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="warning_white.png" width="100" height="100"/>
|
||||
</resources>
|
||||
</document>
|
Loading…
Reference in New Issue