mirror of https://github.com/oxen-io/session-ios
parent
7ede899a74
commit
5f2f8ec6d8
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <SignalServiceKit/TSInteraction.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface OWSContactOffersInteraction : TSInteraction
|
||||||
|
|
||||||
|
//@property (atomic, readonly) BOOL hasMoreUnseenMessages;
|
||||||
|
//
|
||||||
|
//@property (atomic, readonly) NSUInteger missingUnseenSafetyNumberChangeCount;
|
||||||
|
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||||
|
thread:(TSThread *)thread
|
||||||
|
// hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages
|
||||||
|
// missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "OWSContactOffersInteraction.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface OWSContactOffersInteraction ()
|
||||||
|
|
||||||
|
//@property (atomic) BOOL hasMoreUnseenMessages;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
@implementation OWSContactOffersInteraction
|
||||||
|
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
|
{
|
||||||
|
return [super initWithCoder:coder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread
|
||||||
|
// hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages
|
||||||
|
// missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount
|
||||||
|
{
|
||||||
|
self = [super initWithTimestamp:timestamp inThread:thread];
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _hasMoreUnseenMessages = hasMoreUnseenMessages;
|
||||||
|
// _missingUnseenSafetyNumberChangeCount = missingUnseenSafetyNumberChangeCount;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)shouldUseReceiptDateForSorting
|
||||||
|
{
|
||||||
|
// Use the timestamp, not the "received at" timestamp to sort,
|
||||||
|
// since we're creating these interactions after the fact and back-dating them.
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isDynamicInteraction
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <JSQMessagesViewController/JSQMessagesCollectionViewCell.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class OWSContactOffersInteraction;
|
||||||
|
|
||||||
|
@interface OWSContactOffersCell : JSQMessagesCollectionViewCell
|
||||||
|
|
||||||
|
@property (nonatomic, nullable, readonly) OWSContactOffersInteraction *interaction;
|
||||||
|
|
||||||
|
- (void)configureWithInteraction:(OWSContactOffersInteraction *)interaction;
|
||||||
|
|
||||||
|
- (CGSize)bubbleSizeForInteraction:(OWSContactOffersInteraction *)interaction
|
||||||
|
collectionViewWidth:(CGFloat)collectionViewWidth;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,238 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "OWSContactOffersCell.h"
|
||||||
|
#import "NSBundle+JSQMessages.h"
|
||||||
|
#import "OWSContactOffersInteraction.h"
|
||||||
|
#import "UIColor+OWS.h"
|
||||||
|
#import "UIFont+OWS.h"
|
||||||
|
#import "UIView+OWS.h"
|
||||||
|
#import <JSQMessagesViewController/UIView+JSQMessages.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface OWSContactOffersCell ()
|
||||||
|
|
||||||
|
@property (nonatomic, nullable) OWSContactOffersInteraction *interaction;
|
||||||
|
|
||||||
|
@property (nonatomic) UIView *bannerView;
|
||||||
|
@property (nonatomic) UIView *bannerTopHighlightView;
|
||||||
|
@property (nonatomic) UIView *bannerBottomHighlightView1;
|
||||||
|
@property (nonatomic) UIView *bannerBottomHighlightView2;
|
||||||
|
@property (nonatomic) UILabel *titleLabel;
|
||||||
|
@property (nonatomic) UILabel *subtitleLabel;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
@implementation OWSContactOffersCell
|
||||||
|
|
||||||
|
// `[UIView init]` invokes `[self initWithFrame:...]`.
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
if (self = [super initWithFrame:frame]) {
|
||||||
|
[self commontInit];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)commontInit
|
||||||
|
{
|
||||||
|
OWSAssert(!self.bannerView);
|
||||||
|
|
||||||
|
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
|
||||||
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
|
self.bannerView = [UIView new];
|
||||||
|
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
|
||||||
|
[self.contentView addSubview:self.bannerView];
|
||||||
|
|
||||||
|
self.bannerTopHighlightView = [UIView new];
|
||||||
|
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
|
||||||
|
[self.bannerView addSubview:self.bannerTopHighlightView];
|
||||||
|
|
||||||
|
self.bannerBottomHighlightView1 = [UIView new];
|
||||||
|
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
|
||||||
|
[self.bannerView addSubview:self.bannerBottomHighlightView1];
|
||||||
|
|
||||||
|
self.bannerBottomHighlightView2 = [UIView new];
|
||||||
|
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
|
||||||
|
[self.bannerView addSubview:self.bannerBottomHighlightView2];
|
||||||
|
|
||||||
|
self.titleLabel = [UILabel new];
|
||||||
|
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||||
|
self.titleLabel.font = [self titleFont];
|
||||||
|
[self.bannerView addSubview:self.titleLabel];
|
||||||
|
|
||||||
|
self.subtitleLabel = [UILabel new];
|
||||||
|
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
|
||||||
|
self.subtitleLabel.font = [self subtitleFont];
|
||||||
|
// The subtitle may wrap to a second line.
|
||||||
|
self.subtitleLabel.numberOfLines = 0;
|
||||||
|
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||||
|
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||||
|
[self.contentView addSubview:self.subtitleLabel];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)cellReuseIdentifier
|
||||||
|
{
|
||||||
|
return NSStringFromClass([self class]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)configureWithInteraction:(OWSContactOffersInteraction *)interaction;
|
||||||
|
{
|
||||||
|
OWSAssert(interaction);
|
||||||
|
|
||||||
|
_interaction = interaction;
|
||||||
|
|
||||||
|
self.titleLabel.text = [self titleForInteraction:self.interaction];
|
||||||
|
self.subtitleLabel.text = [self subtitleForInteraction:self.interaction];
|
||||||
|
|
||||||
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
|
[self setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIFont *)titleFont
|
||||||
|
{
|
||||||
|
return [UIFont ows_regularFontWithSize:16.f];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIFont *)subtitleFont
|
||||||
|
{
|
||||||
|
return [UIFont ows_regularFontWithSize:12.f];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)titleForInteraction:(OWSContactOffersInteraction *)interaction
|
||||||
|
{
|
||||||
|
return NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR", @"Indicator that separates read from unread messages.")
|
||||||
|
.uppercaseString;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)subtitleForInteraction:(OWSContactOffersInteraction *)interaction
|
||||||
|
{
|
||||||
|
if (!interaction.hasMoreUnseenMessages) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
NSString *subtitleFormat = (interaction.missingUnseenSafetyNumberChangeCount > 0
|
||||||
|
? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT",
|
||||||
|
@"Messages that indicates that there are more unseen messages that be revealed by tapping the 'load "
|
||||||
|
@"earlier messages' button. Embeds {{the name of the 'load earlier messages' button}}")
|
||||||
|
: NSLocalizedString(
|
||||||
|
@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT",
|
||||||
|
@"Messages that indicates that there are more unseen messages including safety number changes that "
|
||||||
|
@"be revealed by tapping the 'load earlier messages' button. Embeds {{the name of the 'load earlier "
|
||||||
|
@"messages' button}}."));
|
||||||
|
NSString *loadMoreButtonName = [NSBundle jsq_localizedStringForKey:@"load_earlier_messages"];
|
||||||
|
return [NSString stringWithFormat:subtitleFormat, loadMoreButtonName];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)subtitleHMargin
|
||||||
|
{
|
||||||
|
return 20.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)subtitleVSpacing
|
||||||
|
{
|
||||||
|
return 3.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)titleInnerHMargin
|
||||||
|
{
|
||||||
|
return 10.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)titleVMargin
|
||||||
|
{
|
||||||
|
return 5.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)topVMargin
|
||||||
|
{
|
||||||
|
return 5.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)bottomVMargin
|
||||||
|
{
|
||||||
|
return 5.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
|
||||||
|
[self.titleLabel sizeToFit];
|
||||||
|
|
||||||
|
// It's a bit of a hack, but we use a view that extends _outside_ the cell's bounds
|
||||||
|
// to draw its background, since we want the background to extend to the edges of the
|
||||||
|
// collection view.
|
||||||
|
//
|
||||||
|
// This layout logic assumes that the cell insets are symmetrical and can be deduced
|
||||||
|
// from the cell frame.
|
||||||
|
CGRect bannerViewFrame = CGRectMake(-self.left,
|
||||||
|
round(self.topVMargin),
|
||||||
|
round(self.width + self.left * 2.f),
|
||||||
|
round(self.titleLabel.height + self.titleVMargin * 2.f));
|
||||||
|
self.bannerView.frame = [self convertRect:bannerViewFrame toView:self.contentView];
|
||||||
|
|
||||||
|
// The highlights should be 1px (not 1pt), so adapt their thickness to
|
||||||
|
// the device resolution.
|
||||||
|
CGFloat kHighlightThickness = 1.f / [UIScreen mainScreen].scale;
|
||||||
|
self.bannerTopHighlightView.frame = CGRectMake(0, 0, self.bannerView.width, kHighlightThickness);
|
||||||
|
self.bannerBottomHighlightView1.frame
|
||||||
|
= CGRectMake(0, self.bannerView.height - kHighlightThickness * 2.f, self.bannerView.width, kHighlightThickness);
|
||||||
|
self.bannerBottomHighlightView2.frame
|
||||||
|
= CGRectMake(0, self.bannerView.height - kHighlightThickness * 1.f, self.bannerView.width, kHighlightThickness);
|
||||||
|
|
||||||
|
[self.titleLabel centerOnSuperview];
|
||||||
|
|
||||||
|
if (self.subtitleLabel.text.length > 0) {
|
||||||
|
CGSize subtitleSize = [self.subtitleLabel
|
||||||
|
sizeThatFits:CGSizeMake(self.contentView.width - [self subtitleHMargin] * 2.f, CGFLOAT_MAX)];
|
||||||
|
self.subtitleLabel.frame = CGRectMake(round((self.contentView.width - subtitleSize.width) * 0.5f),
|
||||||
|
round(self.bannerView.bottom + self.subtitleVSpacing),
|
||||||
|
ceil(subtitleSize.width),
|
||||||
|
ceil(subtitleSize.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGSize)bubbleSizeForInteraction:(OWSContactOffersInteraction *)interaction
|
||||||
|
collectionViewWidth:(CGFloat)collectionViewWidth
|
||||||
|
{
|
||||||
|
CGSize result = CGSizeMake(collectionViewWidth, 0);
|
||||||
|
result.height += self.titleVMargin * 2.f;
|
||||||
|
result.height += self.topVMargin;
|
||||||
|
result.height += self.bottomVMargin;
|
||||||
|
|
||||||
|
NSString *title = [self titleForInteraction:interaction];
|
||||||
|
NSString *subtitle = [self subtitleForInteraction:interaction];
|
||||||
|
|
||||||
|
self.titleLabel.text = title;
|
||||||
|
result.height += ceil([self.titleLabel sizeThatFits:CGSizeZero].height);
|
||||||
|
|
||||||
|
if (subtitle.length > 0) {
|
||||||
|
result.height += self.subtitleVSpacing;
|
||||||
|
|
||||||
|
self.subtitleLabel.text = subtitle;
|
||||||
|
result.height += ceil(
|
||||||
|
[self.subtitleLabel sizeThatFits:CGSizeMake(collectionViewWidth - self.subtitleHMargin * 2.f, CGFLOAT_MAX)]
|
||||||
|
.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareForReuse
|
||||||
|
{
|
||||||
|
[super prepareForReuse];
|
||||||
|
|
||||||
|
self.interaction = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
Loading…
Reference in New Issue