Merge branch 'charlesmchen/conversationCellPerf'
This commit is contained in:
commit
7d3df0bf0a
@ -5,6 +5,7 @@
|
||||
#import "ContactsViewHelper.h"
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSProfileManager.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <SignalServiceKit/Contact.h>
|
||||
@ -205,7 +206,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (NSArray<NSString *> *)searchTermsForSearchString:(NSString *)searchText
|
||||
{
|
||||
return [[[searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
|
||||
return [[[searchText ows_stripped]
|
||||
componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable searchTerm,
|
||||
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||
|
||||
@ -43,6 +43,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId;
|
||||
|
||||
#pragma mark - Caching
|
||||
|
||||
- (NSCache *)cellMediaCache;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@ -54,9 +58,22 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, nullable) ConversationViewItem *viewItem;
|
||||
|
||||
// Cells are prefetched but expensive cells (e.g. media) should only load
|
||||
// when visible and unload when no longer visible. Non-visible cells can
|
||||
// cache their contents on their ConversationViewItem, but that cache may
|
||||
// be evacuated before the cell becomes visible again.
|
||||
//
|
||||
// ConversationViewController also uses this property to evacuate the cell's
|
||||
// meda views when:
|
||||
//
|
||||
// * App enters background.
|
||||
// * Users enters another view (e.g. conversation settings view, call screen, etc.).
|
||||
@property (nonatomic) BOOL isCellVisible;
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth;
|
||||
// The width of the collection view.
|
||||
@property (nonatomic) int contentWidth;
|
||||
|
||||
- (void)loadForDisplay;
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth;
|
||||
|
||||
|
||||
@ -16,9 +16,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
self.viewItem = nil;
|
||||
self.delegate = nil;
|
||||
self.isCellVisible = NO;
|
||||
self.contentWidth = 0;
|
||||
}
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth
|
||||
- (void)loadForDisplay
|
||||
{
|
||||
OWSFail(@"%@ This method should be overridden.", self.logTag);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
isIncoming:(BOOL)isIncoming
|
||||
viewItem:(ConversationViewItem *)viewItem;
|
||||
|
||||
- (void)createContentsForSize:(CGSize)viewSize;
|
||||
- (void)createContents;
|
||||
|
||||
+ (CGFloat)bubbleHeight;
|
||||
|
||||
|
||||
@ -193,34 +193,45 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return (self.attachmentStream.isVoiceMessage || self.attachmentStream.sourceFilename.length < 1);
|
||||
}
|
||||
|
||||
- (void)createContentsForSize:(CGSize)viewSize
|
||||
- (void)createContents
|
||||
{
|
||||
UIColor *textColor = [self audioTextColor];
|
||||
|
||||
self.backgroundColor = self.bubbleBackgroundColor;
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
// TODO: Verify that this layout works in RTL.
|
||||
const CGFloat kBubbleTailWidth = 6.f;
|
||||
CGRect contentFrame = CGRectMake(self.isIncoming ? kBubbleTailWidth : 0.f,
|
||||
self.audioIconVMargin,
|
||||
viewSize.width - kBubbleTailWidth - self.audioIconHMargin,
|
||||
viewSize.height - self.audioIconVMargin * 2);
|
||||
|
||||
CGRect iconFrame = CGRectMake((CGFloat)round(contentFrame.origin.x + self.audioIconHMargin),
|
||||
(CGFloat)round(contentFrame.origin.y + (contentFrame.size.height - self.iconSize) * 0.5f),
|
||||
self.iconSize,
|
||||
self.iconSize);
|
||||
_audioPlayPauseButton = [[UIButton alloc] initWithFrame:iconFrame];
|
||||
_audioPlayPauseButton.enabled = NO;
|
||||
[self addSubview:_audioPlayPauseButton];
|
||||
UIView *contentView = [UIView containerView];
|
||||
[self addSubview:contentView];
|
||||
[contentView autoPinLeadingToSuperviewWithMargin:self.isIncoming ? kBubbleTailWidth : 0.f];
|
||||
[contentView autoPinTrailingToSuperviewWithMargin:self.isIncoming ? 0.f : kBubbleTailWidth];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.audioIconVMargin];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.audioIconVMargin];
|
||||
|
||||
_audioPlayPauseButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.audioPlayPauseButton.enabled = NO;
|
||||
[contentView addSubview:self.audioPlayPauseButton];
|
||||
[self.audioPlayPauseButton autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:self.audioIconHMargin];
|
||||
[self.audioPlayPauseButton autoVCenterInSuperview];
|
||||
[self.audioPlayPauseButton autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[self.audioPlayPauseButton autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
|
||||
const CGFloat kLabelHSpacing = self.audioIconHSpacing;
|
||||
|
||||
UIView *labelsView = [UIView containerView];
|
||||
[contentView addSubview:labelsView];
|
||||
[labelsView autoPinLeadingToTrailingOfView:self.audioPlayPauseButton margin:kLabelHSpacing];
|
||||
[labelsView autoPinEdgeToSuperviewEdge:ALEdgeRight];
|
||||
[labelsView autoVCenterInSuperview];
|
||||
|
||||
const CGFloat kLabelVSpacing = 2;
|
||||
NSString *filename = self.attachmentStream.sourceFilename;
|
||||
if (!filename) {
|
||||
filename = [[self.attachmentStream filePath] lastPathComponent];
|
||||
}
|
||||
NSString *topText = [[filename stringByDeletingPathExtension]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *topText = [[filename stringByDeletingPathExtension] ows_stripped];
|
||||
if (topText.length < 1) {
|
||||
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].uppercaseString;
|
||||
}
|
||||
@ -235,13 +246,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
topLabel.textColor = [textColor colorWithAlphaComponent:0.85f];
|
||||
topLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
topLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 13.f)];
|
||||
[topLabel sizeToFit];
|
||||
[self addSubview:topLabel];
|
||||
[labelsView addSubview:topLabel];
|
||||
[topLabel autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
||||
[topLabel autoPinWidthToSuperview];
|
||||
|
||||
const CGFloat kAudioProgressViewHeight = 12.f;
|
||||
AudioProgressView *audioProgressView = [AudioProgressView new];
|
||||
self.audioProgressView = audioProgressView;
|
||||
[self updateAudioProgressView];
|
||||
[self addSubview:audioProgressView];
|
||||
[labelsView addSubview:audioProgressView];
|
||||
[audioProgressView autoPinWidthToSuperview];
|
||||
[audioProgressView autoSetDimension:ALDimensionHeight toSize:kAudioProgressViewHeight];
|
||||
[audioProgressView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topLabel withOffset:kLabelVSpacing];
|
||||
|
||||
UILabel *bottomLabel = [UILabel new];
|
||||
self.audioBottomLabel = bottomLabel;
|
||||
@ -249,25 +265,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
bottomLabel.textColor = [textColor colorWithAlphaComponent:0.85f];
|
||||
bottomLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
bottomLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 13.f)];
|
||||
[bottomLabel sizeToFit];
|
||||
[self addSubview:bottomLabel];
|
||||
|
||||
const CGFloat topLabelHeight = (CGFloat)ceil(topLabel.font.lineHeight);
|
||||
const CGFloat kAudioProgressViewHeight = 12.f;
|
||||
const CGFloat bottomLabelHeight = (CGFloat)ceil(bottomLabel.font.lineHeight);
|
||||
CGRect labelsBounds = CGRectZero;
|
||||
labelsBounds.origin.x = (CGFloat)round(iconFrame.origin.x + iconFrame.size.width + kLabelHSpacing);
|
||||
labelsBounds.size.width = contentFrame.origin.x + contentFrame.size.width - labelsBounds.origin.x;
|
||||
labelsBounds.size.height = topLabelHeight + kAudioProgressViewHeight + bottomLabelHeight + kLabelVSpacing * 2;
|
||||
labelsBounds.origin.y
|
||||
= (CGFloat)round(contentFrame.origin.y + (contentFrame.size.height - labelsBounds.size.height) * 0.5f);
|
||||
|
||||
CGFloat y = labelsBounds.origin.y;
|
||||
topLabel.frame = CGRectMake(labelsBounds.origin.x, labelsBounds.origin.y, labelsBounds.size.width, topLabelHeight);
|
||||
y += topLabelHeight + kLabelVSpacing;
|
||||
audioProgressView.frame = CGRectMake(labelsBounds.origin.x, y, labelsBounds.size.width, kAudioProgressViewHeight);
|
||||
y += kAudioProgressViewHeight + kLabelVSpacing;
|
||||
bottomLabel.frame = CGRectMake(labelsBounds.origin.x, y, labelsBounds.size.width, bottomLabelHeight);
|
||||
[labelsView addSubview:bottomLabel];
|
||||
[bottomLabel autoPinWidthToSuperview];
|
||||
[bottomLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:audioProgressView withOffset:kLabelVSpacing];
|
||||
[bottomLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
|
||||
|
||||
[self updateContents];
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth
|
||||
- (void)loadForDisplay
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]);
|
||||
|
||||
@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming;
|
||||
|
||||
- (void)createContentsForSize:(CGSize)viewSize;
|
||||
- (void)createContents;
|
||||
|
||||
+ (CGFloat)bubbleHeight;
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "OWSGenericAttachmentView.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "UIColor+JSQMessages.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
@ -98,32 +99,43 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return [self.textColor blendWithColor:self.bubbleBackgroundColor alpha:alpha];
|
||||
}
|
||||
|
||||
- (void)createContentsForSize:(CGSize)viewSize
|
||||
- (void)createContents
|
||||
{
|
||||
UIColor *textColor = (self.isIncoming ? [UIColor colorWithWhite:0.2 alpha:1.f] : [UIColor whiteColor]);
|
||||
|
||||
self.backgroundColor = self.bubbleBackgroundColor;
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
// TODO: Verify that this layout works in RTL.
|
||||
const CGFloat kBubbleTailWidth = 6.f;
|
||||
CGRect contentFrame = CGRectMake(self.isIncoming ? kBubbleTailWidth : 0.f,
|
||||
self.vMargin,
|
||||
viewSize.width - kBubbleTailWidth - self.iconHMargin,
|
||||
viewSize.height - self.vMargin * 2);
|
||||
|
||||
UIView *contentView = [UIView containerView];
|
||||
[self addSubview:contentView];
|
||||
[contentView autoPinLeadingToSuperviewWithMargin:self.isIncoming ? kBubbleTailWidth : 0.f];
|
||||
[contentView autoPinTrailingToSuperviewWithMargin:self.isIncoming ? 0.f : kBubbleTailWidth];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin];
|
||||
|
||||
UIImage *image = [UIImage imageNamed:@"generic-attachment-small"];
|
||||
OWSAssert(image);
|
||||
UIImageView *imageView = [UIImageView new];
|
||||
CGRect iconFrame = CGRectMake(round(contentFrame.origin.x + self.iconHMargin),
|
||||
round(contentFrame.origin.y + (contentFrame.size.height - self.iconSize) * 0.5f),
|
||||
self.iconSize,
|
||||
self.iconSize);
|
||||
imageView.frame = iconFrame;
|
||||
imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
imageView.tintColor = self.bubbleBackgroundColor;
|
||||
imageView.backgroundColor
|
||||
= (self.isIncoming ? [UIColor colorWithRGBHex:0x9e9e9e] : [self foregroundColorWithOpacity:0.15f]);
|
||||
imageView.layer.cornerRadius = MIN(imageView.bounds.size.width, imageView.bounds.size.height) * 0.5f;
|
||||
[self addSubview:imageView];
|
||||
[contentView addSubview:imageView];
|
||||
[imageView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:self.iconHMargin];
|
||||
[imageView autoVCenterInSuperview];
|
||||
[imageView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[imageView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
|
||||
const CGFloat kLabelHSpacing = self.iconHSpacing;
|
||||
UIView *labelsView = [UIView containerView];
|
||||
[contentView addSubview:labelsView];
|
||||
[labelsView autoPinLeadingToTrailingOfView:imageView margin:kLabelHSpacing];
|
||||
[labelsView autoPinEdgeToSuperviewEdge:ALEdgeRight];
|
||||
[labelsView autoVCenterInSuperview];
|
||||
|
||||
NSString *filename = self.attachmentStream.sourceFilename;
|
||||
if (!filename) {
|
||||
@ -145,22 +157,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
fileTypeLabel.font = [UIFont ows_mediumFontWithSize:20.f];
|
||||
fileTypeLabel.adjustsFontSizeToFitWidth = YES;
|
||||
fileTypeLabel.textAlignment = NSTextAlignmentCenter;
|
||||
CGRect fileTypeLabelFrame = CGRectZero;
|
||||
fileTypeLabelFrame.size = [fileTypeLabel sizeThatFits:CGSizeZero];
|
||||
// This dimension depends on the space within the icon boundaries.
|
||||
fileTypeLabelFrame.size.width = 15.f;
|
||||
// Center on icon.
|
||||
fileTypeLabelFrame.origin.x
|
||||
= round(iconFrame.origin.x + (iconFrame.size.width - fileTypeLabelFrame.size.width) * 0.5f);
|
||||
fileTypeLabelFrame.origin.y
|
||||
= round(iconFrame.origin.y + (iconFrame.size.height - fileTypeLabelFrame.size.height) * 0.5f);
|
||||
fileTypeLabel.frame = fileTypeLabelFrame;
|
||||
[self addSubview:fileTypeLabel];
|
||||
[imageView addSubview:fileTypeLabel];
|
||||
[imageView autoCenterInSuperview];
|
||||
[imageView autoSetDimension:ALDimensionWidth toSize:15.f];
|
||||
|
||||
const CGFloat kLabelHSpacing = self.iconHSpacing;
|
||||
const CGFloat kLabelVSpacing = 2;
|
||||
NSString *topText =
|
||||
[self.attachmentStream.sourceFilename stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
NSString *topText = [self.attachmentStream.sourceFilename ows_stripped];
|
||||
if (topText.length < 1) {
|
||||
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].uppercaseString;
|
||||
}
|
||||
@ -172,8 +175,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
topLabel.textColor = textColor;
|
||||
topLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
topLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 15.f)];
|
||||
[topLabel sizeToFit];
|
||||
[self addSubview:topLabel];
|
||||
[labelsView addSubview:topLabel];
|
||||
[topLabel autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
||||
[topLabel autoPinWidthToSuperview];
|
||||
|
||||
NSError *error;
|
||||
unsigned long long fileSize =
|
||||
@ -185,21 +189,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
bottomLabel.textColor = [textColor colorWithAlphaComponent:0.85f];
|
||||
bottomLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
bottomLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 13.f)];
|
||||
[bottomLabel sizeToFit];
|
||||
[self addSubview:bottomLabel];
|
||||
|
||||
CGRect topLabelFrame = CGRectZero;
|
||||
topLabelFrame.size = topLabel.bounds.size;
|
||||
topLabelFrame.origin.x = round(iconFrame.origin.x + iconFrame.size.width + kLabelHSpacing);
|
||||
topLabelFrame.origin.y = round(contentFrame.origin.y
|
||||
+ (contentFrame.size.height - (topLabel.frame.size.height + bottomLabel.frame.size.height + kLabelVSpacing))
|
||||
* 0.5f);
|
||||
topLabelFrame.size.width = round((contentFrame.origin.x + contentFrame.size.width) - topLabelFrame.origin.x);
|
||||
topLabel.frame = topLabelFrame;
|
||||
|
||||
CGRect bottomLabelFrame = topLabelFrame;
|
||||
bottomLabelFrame.origin.y += topLabelFrame.size.height + kLabelVSpacing;
|
||||
bottomLabel.frame = bottomLabelFrame;
|
||||
[labelsView addSubview:bottomLabel];
|
||||
[bottomLabel autoPinWidthToSuperview];
|
||||
[bottomLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topLabel withOffset:kLabelVSpacing];
|
||||
[bottomLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -207,7 +207,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return (TSMessage *)self.viewItem.interaction;
|
||||
}
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth
|
||||
- (void)loadForDisplay
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert(self.viewItem.interaction);
|
||||
@ -220,7 +220,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
= isIncoming ? [self.bubbleFactory incoming] : [self.bubbleFactory outgoing];
|
||||
self.bubbleImageView.image = bubbleImageData.messageBubbleImage;
|
||||
|
||||
[self updateDateHeader:contentWidth];
|
||||
[self updateDateHeader];
|
||||
[self updateFooter];
|
||||
|
||||
switch (self.cellType) {
|
||||
@ -243,7 +243,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
case OWSMessageCellType_GenericAttachment: {
|
||||
self.attachmentView =
|
||||
[[OWSGenericAttachmentView alloc] initWithAttachment:self.attachmentStream isIncoming:self.isIncoming];
|
||||
[self.attachmentView createContentsForSize:self.bounds.size];
|
||||
[self.attachmentView createContents];
|
||||
[self replaceBubbleWithView:self.attachmentView];
|
||||
[self addAttachmentUploadViewIfNecessary:self.attachmentView];
|
||||
break;
|
||||
@ -254,6 +254,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
[self ensureViewMediaState];
|
||||
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// NSLog(@"---- %@", self.viewItem.interaction.debugDescription);
|
||||
// NSLog(@"cell: %@", NSStringFromCGRect(self.frame));
|
||||
@ -263,8 +265,113 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// });
|
||||
}
|
||||
|
||||
- (void)updateDateHeader:(int)contentWidth
|
||||
// We now eagerly create out view hierarchy (to do this exactly once per cell usage)
|
||||
// but lazy-load any expensive media (photo, gif, etc.) used in those views. Note that
|
||||
// this lazy-load can fail, in which case we modify the view hierarchy to use an "error"
|
||||
// state. The didCellMediaFailToLoad reflects media load fails.
|
||||
- (nullable id)tryToLoadCellMedia:(nullable id (^)())loadCellMediaBlock
|
||||
mediaView:(UIView *)mediaView
|
||||
cacheKey:(NSString *)cacheKey
|
||||
{
|
||||
OWSAssert(self.attachmentStream);
|
||||
OWSAssert(mediaView);
|
||||
OWSAssert(cacheKey);
|
||||
|
||||
if (self.viewItem.didCellMediaFailToLoad) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSCache *cellMediaCache = self.delegate.cellMediaCache;
|
||||
OWSAssert(cellMediaCache);
|
||||
|
||||
id _Nullable cellMedia = [cellMediaCache objectForKey:cacheKey];
|
||||
if (cellMedia) {
|
||||
DDLogVerbose(@"%@ cell media cache hit", self.logTag);
|
||||
return cellMedia;
|
||||
}
|
||||
cellMedia = loadCellMediaBlock();
|
||||
if (cellMedia) {
|
||||
DDLogVerbose(@"%@ cell media cache miss", self.logTag);
|
||||
[cellMediaCache setObject:cellMedia forKey:cacheKey];
|
||||
} else {
|
||||
DDLogError(@"%@ Failed to load cell media: %@", [self logTag], [self.attachmentStream mediaURL]);
|
||||
self.viewItem.didCellMediaFailToLoad = YES;
|
||||
[mediaView removeFromSuperview];
|
||||
// TODO: We need to hide/remove the media view.
|
||||
[self showAttachmentErrorView];
|
||||
}
|
||||
return cellMedia;
|
||||
}
|
||||
|
||||
// We want to lazy-load expensive view contents and eagerly unload if the
|
||||
// cell is no longer visible.
|
||||
- (void)ensureViewMediaState
|
||||
{
|
||||
if (!self.isCellVisible) {
|
||||
// Eagerly unload.
|
||||
self.stillImageView.image = nil;
|
||||
self.animatedImageView.image = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.cellType) {
|
||||
case OWSMessageCellType_StillImage: {
|
||||
if (self.stillImageView.image) {
|
||||
return;
|
||||
}
|
||||
self.stillImageView.image = [self tryToLoadCellMedia:^{
|
||||
OWSAssert([self.attachmentStream isImage]);
|
||||
return self.attachmentStream.image;
|
||||
}
|
||||
mediaView:self.stillImageView
|
||||
cacheKey:self.attachmentStream.uniqueId];
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_AnimatedImage: {
|
||||
if (self.animatedImageView.image) {
|
||||
return;
|
||||
}
|
||||
self.animatedImageView.image = [self tryToLoadCellMedia:^{
|
||||
OWSAssert([self.attachmentStream isAnimated]);
|
||||
|
||||
NSString *_Nullable filePath = [self.attachmentStream filePath];
|
||||
YYImage *_Nullable animatedImage = nil;
|
||||
if (filePath && [NSData ows_isValidImageAtPath:filePath]) {
|
||||
animatedImage = [YYImage imageWithContentsOfFile:filePath];
|
||||
}
|
||||
return animatedImage;
|
||||
}
|
||||
mediaView:self.animatedImageView
|
||||
cacheKey:self.attachmentStream.uniqueId];
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_Video: {
|
||||
if (self.stillImageView.image) {
|
||||
return;
|
||||
}
|
||||
self.stillImageView.image = [self tryToLoadCellMedia:^{
|
||||
OWSAssert([self.attachmentStream isVideo]);
|
||||
|
||||
return self.attachmentStream.image;
|
||||
}
|
||||
mediaView:self.stillImageView
|
||||
cacheKey:self.attachmentStream.uniqueId];
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_TextMessage:
|
||||
case OWSMessageCellType_OversizeTextMessage:
|
||||
case OWSMessageCellType_GenericAttachment:
|
||||
case OWSMessageCellType_DownloadingAttachment:
|
||||
case OWSMessageCellType_Audio:
|
||||
// Inexpensive cell types don't need to lazy-load or eagerly-unload.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDateHeader
|
||||
{
|
||||
OWSAssert(self.contentWidth > 0);
|
||||
|
||||
static NSDateFormatter *dateHeaderDateFormatter = nil;
|
||||
static NSDateFormatter *dateHeaderTimeFormatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
@ -312,7 +419,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
self.dateHeaderConstraints = @[
|
||||
// Date headers should be visually centered within the conversation view,
|
||||
// so they need to extend outside the cell's boundaries.
|
||||
[self.dateHeaderLabel autoSetDimension:ALDimensionWidth toSize:contentWidth],
|
||||
[self.dateHeaderLabel autoSetDimension:ALDimensionWidth toSize:self.contentWidth],
|
||||
(self.isIncoming ? [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeLeading]
|
||||
: [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTrailing]),
|
||||
[self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTop],
|
||||
@ -452,14 +559,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
OWSAssert(self.attachmentStream);
|
||||
OWSAssert([self.attachmentStream isImage]);
|
||||
|
||||
UIImage *_Nullable image = self.attachmentStream.image;
|
||||
if (!image) {
|
||||
DDLogError(@"%@ Could not load image: %@", [self logTag], [self.attachmentStream mediaURL]);
|
||||
[self showAttachmentErrorView];
|
||||
return;
|
||||
}
|
||||
|
||||
self.stillImageView = [[UIImageView alloc] initWithImage:image];
|
||||
self.stillImageView = [UIImageView new];
|
||||
// We need to specify a contentMode since the size of the image
|
||||
// might not match the aspect ratio of the view.
|
||||
self.stillImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
@ -476,19 +576,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
OWSAssert(self.attachmentStream);
|
||||
OWSAssert([self.attachmentStream isAnimated]);
|
||||
|
||||
NSString *_Nullable filePath = [self.attachmentStream filePath];
|
||||
YYImage *_Nullable animatedImage = nil;
|
||||
if (filePath && [NSData ows_isValidImageAtPath:filePath]) {
|
||||
animatedImage = [YYImage imageWithContentsOfFile:filePath];
|
||||
}
|
||||
if (!animatedImage) {
|
||||
DDLogError(@"%@ Could not load animated image: %@", [self logTag], [self.attachmentStream mediaURL]);
|
||||
[self showAttachmentErrorView];
|
||||
return;
|
||||
}
|
||||
|
||||
self.animatedImageView = [[YYAnimatedImageView alloc] init];
|
||||
self.animatedImageView.image = animatedImage;
|
||||
// We need to specify a contentMode since the size of the image
|
||||
// might not match the aspect ratio of the view.
|
||||
self.animatedImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
@ -505,7 +593,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
isIncoming:self.isIncoming
|
||||
viewItem:self.viewItem];
|
||||
self.viewItem.lastAudioMessageView = self.audioMessageView;
|
||||
[self.audioMessageView createContentsForSize:self.bounds.size];
|
||||
[self.audioMessageView createContents];
|
||||
[self replaceBubbleWithView:self.audioMessageView];
|
||||
[self addAttachmentUploadViewIfNecessary:self.audioMessageView];
|
||||
}
|
||||
@ -515,16 +603,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
OWSAssert(self.attachmentStream);
|
||||
OWSAssert([self.attachmentStream isVideo]);
|
||||
|
||||
// CGSize size = [self mediaViewDisplaySize];
|
||||
|
||||
UIImage *_Nullable image = self.attachmentStream.image;
|
||||
if (!image) {
|
||||
DDLogError(@"%@ Could not load image: %@", [self logTag], [self.attachmentStream mediaURL]);
|
||||
[self showAttachmentErrorView];
|
||||
return;
|
||||
}
|
||||
|
||||
self.stillImageView = [[UIImageView alloc] initWithImage:image];
|
||||
self.stillImageView = [UIImageView new];
|
||||
// We need to specify a contentMode since the size of the image
|
||||
// might not match the aspect ratio of the view.
|
||||
self.stillImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
@ -614,25 +693,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[self.payloadView updateMask];
|
||||
}
|
||||
|
||||
//// TODO:
|
||||
//- (void)setFrame:(CGRect)frame {
|
||||
// [super setFrame:frame];
|
||||
//
|
||||
// DDLogError(@"setFrame: %@ %@ %@", self.viewItem.interaction.uniqueId, self.viewItem.interaction.description,
|
||||
// NSStringFromCGRect(frame));
|
||||
//}
|
||||
//
|
||||
//// TODO:
|
||||
//- (void)setBounds:(CGRect)bounds {
|
||||
// [super setBounds:bounds];
|
||||
//
|
||||
// DDLogError(@"setBounds: %@ %@ %@", self.viewItem.interaction.uniqueId, self.viewItem.interaction.description,
|
||||
// NSStringFromCGRect(bounds));
|
||||
//}
|
||||
|
||||
- (void)showAttachmentErrorView
|
||||
{
|
||||
// TODO: We could do a better job of indicating that the image could not be loaded.
|
||||
OWSAssert(!self.customView);
|
||||
|
||||
// TODO: We could do a better job of indicating that the media could not be loaded.
|
||||
self.customView = [UIView new];
|
||||
self.customView.backgroundColor = [UIColor colorWithWhite:0.85f alpha:1.f];
|
||||
self.customView.userInteractionEnabled = NO;
|
||||
@ -860,6 +925,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return;
|
||||
}
|
||||
|
||||
[self ensureViewMediaState];
|
||||
|
||||
if (isCellVisible) {
|
||||
if (self.message.shouldStartExpireTimer) {
|
||||
[self.expirationTimerView ensureAnimations];
|
||||
|
||||
@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth
|
||||
- (void)loadForDisplay
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay:(int)contentWidth
|
||||
- (void)loadForDisplay
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]);
|
||||
|
||||
@ -57,6 +57,36 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[super setContentOffset:contentOffset];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
DDLogVerbose(@"%@ reloadData", self.logTag);
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections
|
||||
{
|
||||
DDLogVerbose(@"%@ reloadSections", self.logTag);
|
||||
[super reloadSections:sections];
|
||||
}
|
||||
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
DDLogVerbose(@"%@ reloadItemsAtIndexPaths", self.logTag);
|
||||
[super reloadItemsAtIndexPaths:indexPaths];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "ConversationInputTextView.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "Signal-Swift.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@ -121,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (NSString *)trimmedText
|
||||
{
|
||||
return [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
return [self.text ows_stripped];
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#import "FingerprintViewController.h"
|
||||
#import "FullImageViewController.h"
|
||||
#import "NSAttributedString+OWS.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "NewGroupViewController.h"
|
||||
#import "OWSAudioAttachmentPlayer.h"
|
||||
#import "OWSContactOffersCell.h"
|
||||
@ -165,7 +166,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
@property (nonatomic, nullable) OWSAudioAttachmentPlayer *audioAttachmentPlayer;
|
||||
@property (nonatomic, nullable) NSUUID *voiceMessageUUID;
|
||||
|
||||
@property (nonatomic) NSTimer *readTimer;
|
||||
@property (nonatomic, nullable) NSTimer *readTimer;
|
||||
@property (nonatomic) NSCache *cellMediaCache;
|
||||
@property (nonatomic) ConversationHeaderView *navigationBarTitleView;
|
||||
@property (nonatomic) UILabel *navigationBarTitleLabel;
|
||||
@property (nonatomic) UILabel *navigationBarSubtitleLabel;
|
||||
@ -392,6 +394,9 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
_isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
|
||||
_composeOnOpen = keyboardOnViewAppearing;
|
||||
_callOnOpen = callOnViewAppearing;
|
||||
_cellMediaCache = [NSCache new];
|
||||
// Cache the cell media for ~24 cells.
|
||||
self.cellMediaCache.countLimit = 24;
|
||||
|
||||
[self.uiDatabaseConnection beginLongLivedReadTransaction];
|
||||
|
||||
@ -526,6 +531,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
self.hasClearedUnreadMessagesIndicator = NO;
|
||||
[self.dynamicInteractions clearUnreadIndicatorState];
|
||||
}
|
||||
[self.cellMediaCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification
|
||||
@ -943,6 +949,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
- (void)cancelReadTimer
|
||||
{
|
||||
[self.readTimer invalidate];
|
||||
self.readTimer = nil;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
@ -986,6 +993,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
[self saveDraft];
|
||||
[self markVisibleMessagesAsRead];
|
||||
[self cancelVoiceMemo];
|
||||
[self.cellMediaCache removeAllObjects];
|
||||
|
||||
self.isUserScrolling = NO;
|
||||
}
|
||||
@ -3630,7 +3638,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
text = [text ows_stripped];
|
||||
|
||||
if (text.length < 1) {
|
||||
return;
|
||||
@ -4025,8 +4033,9 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
||||
}
|
||||
cell.viewItem = viewItem;
|
||||
cell.delegate = self;
|
||||
cell.contentWidth = self.layout.contentWidth;
|
||||
|
||||
[cell loadForDisplay:self.layout.contentWidth];
|
||||
[cell loadForDisplay];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
@ -86,6 +86,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
||||
- (nullable TSAttachmentPointer *)attachmentPointer;
|
||||
- (CGSize)contentSize;
|
||||
|
||||
// We don't want to try to load the media for this item (if any)
|
||||
// if a load has previously failed.
|
||||
@property (nonatomic) BOOL didCellMediaFailToLoad;
|
||||
|
||||
// TODO:
|
||||
//// Cells will request that this adapter clear its cached media views,
|
||||
//// but the adapter should only honor requests from the last cell to
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "ConversationViewItem.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSAudioMessageView.h"
|
||||
#import "OWSContactOffersCell.h"
|
||||
#import "OWSMessageCell.h"
|
||||
@ -274,14 +275,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
||||
if (!displayableText) {
|
||||
// Only show up to 2kb of text.
|
||||
const NSUInteger kMaxTextDisplayLength = 2 * 1024;
|
||||
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
text = [text ows_stripped];
|
||||
displayableText = [[DisplayableTextFilter new] displayableText:text];
|
||||
if (displayableText.length > kMaxTextDisplayLength) {
|
||||
// Trim whitespace before _AND_ after slicing the snipper from the string.
|
||||
NSString *snippet =
|
||||
[[[displayableText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
|
||||
substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *snippet = [
|
||||
[[displayableText ows_stripped] substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] ows_stripped];
|
||||
displayableText = [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT",
|
||||
@"A display format for oversize text messages."),
|
||||
snippet];
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "CountryCodeViewController.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "PhoneNumberUtil.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
@ -142,8 +143,7 @@
|
||||
|
||||
- (void)searchTextDidChange
|
||||
{
|
||||
NSString *searchText =
|
||||
[self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *searchText = [self.searchBar.text ows_stripped];
|
||||
|
||||
self.countryCodes = [PhoneNumberUtil countryCodesForSearchTerm:searchText];
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "ContactsViewHelper.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "OWSTableViewController.h"
|
||||
@ -515,8 +516,7 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68;
|
||||
|
||||
- (TSGroupModel *)makeGroup
|
||||
{
|
||||
NSString *groupName = [self.groupNameTextField.text
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *groupName = [self.groupNameTextField.text ows_stripped];
|
||||
NSMutableArray<NSString *> *recipientIds = [self.memberRecipientIds.allObjects mutableCopy];
|
||||
[recipientIds addObject:[self.contactsViewHelper localNumber]];
|
||||
NSData *groupId = [SecurityUtils generateRandomBytes:16];
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#import "AppDelegate.h"
|
||||
#import "AvatarViewHelper.h"
|
||||
#import "HomeViewController.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "OWSProfileManager.h"
|
||||
#import "Signal-Swift.h"
|
||||
@ -410,7 +411,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
|
||||
|
||||
- (NSString *)normalizedProfileName
|
||||
{
|
||||
return [self.nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
return [self.nameTextField.text ows_stripped];
|
||||
}
|
||||
|
||||
- (void)updateProfileCompleted
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#import "CodeVerificationViewController.h"
|
||||
#import "CountryCodeViewController.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "PhoneNumber.h"
|
||||
#import "PhoneNumberUtil.h"
|
||||
#import "Signal-Swift.h"
|
||||
@ -280,8 +281,7 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi
|
||||
|
||||
- (void)sendCodeAction
|
||||
{
|
||||
NSString *phoneNumberText =
|
||||
[_phoneNumberTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *phoneNumberText = [_phoneNumberTextField.text ows_stripped];
|
||||
if (phoneNumberText.length < 1) {
|
||||
[OWSAlerts
|
||||
showAlertWithTitle:NSLocalizedString(@"REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_TITLE",
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "ContactsViewHelper.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSContactsSearcher.h"
|
||||
#import "OWSTableViewController.h"
|
||||
@ -223,8 +224,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
{
|
||||
NSArray<TSThread *> *threads = self.threadViewHelper.threads;
|
||||
|
||||
NSString *searchTerm =
|
||||
[[self.searchBar text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *searchTerm = [[self.searchBar text] ows_stripped];
|
||||
|
||||
if ([searchTerm isEqualToString:@""]) {
|
||||
return threads;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#import "SendExternalFileViewController.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "ThreadUtil.h"
|
||||
#import "UIColor+OWS.h"
|
||||
@ -141,8 +142,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// TODO: If we reuse this VC, for example to offer a "forward attachment to other thread",
|
||||
// feature, this assumption would no longer apply.
|
||||
OWSAssert(self.attachment);
|
||||
NSString *filename =
|
||||
[self.attachment.sourceFilename stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
NSString *filename = [self.attachment.sourceFilename ows_stripped];
|
||||
OWSAssert(filename.length > 0);
|
||||
const NSUInteger kMaxFilenameLength = 20;
|
||||
if (filename.length > kMaxFilenameLength) {
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "ContactsViewHelper.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "OWSTableViewController.h"
|
||||
@ -196,8 +197,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
UITextField *groupNameTextField = [UITextField new];
|
||||
_groupNameTextField = groupNameTextField;
|
||||
self.groupNameTextField.text = [self.thread.groupModel.groupName
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
self.groupNameTextField.text = [self.thread.groupModel.groupName ows_stripped];
|
||||
groupNameTextField.textColor = [UIColor blackColor];
|
||||
groupNameTextField.font = [UIFont ows_dynamicTypeTitle2Font];
|
||||
groupNameTextField.placeholder
|
||||
@ -373,8 +373,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
{
|
||||
OWSAssert(self.conversationSettingsViewDelegate);
|
||||
|
||||
NSString *groupName = [self.groupNameTextField.text
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *groupName = [self.groupNameTextField.text ows_stripped];
|
||||
TSGroupModel *groupModel = [[TSGroupModel alloc] initWithTitle:groupName
|
||||
memberIds:[self.memberRecipientIds.allObjects mutableCopy]
|
||||
image:self.groupAvatar
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "OWSContactsSearcher.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import <SignalServiceKit/PhoneNumber.h>
|
||||
|
||||
@interface OWSContactsSearcher ()
|
||||
@ -22,7 +23,7 @@
|
||||
}
|
||||
|
||||
- (NSArray<Contact *> *)filterWithString:(NSString *)string {
|
||||
NSString *searchTerm = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *searchTerm = [string ows_stripped];
|
||||
|
||||
if ([searchTerm isEqualToString:@""]) {
|
||||
return self.contacts;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#import "NotificationsManager.h"
|
||||
#import "Environment.h"
|
||||
#import "NSString+OWS.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSPreferences.h"
|
||||
#import "PushManager.h"
|
||||
@ -272,8 +273,7 @@ NSString *const kNotificationsManagerNewMesssageSoundName = @"NewMessage.aifc";
|
||||
BOOL shouldPlaySound = [self shouldPlaySoundForNotification];
|
||||
|
||||
NSString *senderName = [contactsManager displayNameForPhoneIdentifier:message.authorId];
|
||||
NSString *groupName =
|
||||
[thread.name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *groupName = [thread.name ows_stripped];
|
||||
if (groupName.length < 1) {
|
||||
groupName = [MessageStrings newGroupDefaultTitle];
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user