mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			243 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Objective-C
		
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Objective-C
		
	
| //
 | |
| //  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| #import "OWSGenericAttachmentView.h"
 | |
| #import "OWSBezierPathView.h"
 | |
| #import "Session-Swift.h"
 | |
| #import "UIFont+OWS.h"
 | |
| #import "UIView+OWS.h"
 | |
| 
 | |
| #import <SignalUtilitiesKit/OWSFormat.h>
 | |
| #import <SignalUtilitiesKit/UIColor+OWS.h>
 | |
| #import <SessionUtilitiesKit/MIMETypeUtil.h>
 | |
| #import <SessionUtilitiesKit/NSString+SSK.h>
 | |
| #import <SessionMessagingKit/TSAttachmentStream.h>
 | |
| #import <SignalCoreKit/NSString+OWS.h>
 | |
| 
 | |
| NS_ASSUME_NONNULL_BEGIN
 | |
| 
 | |
| @interface OWSGenericAttachmentView ()
 | |
| 
 | |
| @property (nonatomic) TSAttachment *attachment;
 | |
| @property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
 | |
| @property (nonatomic, weak) id<ConversationViewItem> viewItem;
 | |
| @property (nonatomic) BOOL isIncoming;
 | |
| @property (nonatomic) UILabel *topLabel;
 | |
| @property (nonatomic) UILabel *bottomLabel;
 | |
| 
 | |
| @end
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| @implementation OWSGenericAttachmentView
 | |
| 
 | |
| - (instancetype)initWithAttachment:(TSAttachment *)attachment
 | |
|                         isIncoming:(BOOL)isIncoming
 | |
|                           viewItem:(id<ConversationViewItem>)viewItem
 | |
| {
 | |
|     self = [super init];
 | |
| 
 | |
|     if (self) {
 | |
|         _attachment = attachment;
 | |
|         if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
 | |
|             _attachmentStream = (TSAttachmentStream *)attachment;
 | |
|         }
 | |
|         _isIncoming = isIncoming;
 | |
|         _viewItem = viewItem;
 | |
|     }
 | |
| 
 | |
|     return self;
 | |
| }
 | |
| 
 | |
| #pragma mark -
 | |
| 
 | |
| - (CGFloat)hMargin
 | |
| {
 | |
|     return 0.f;
 | |
| }
 | |
| 
 | |
| - (CGFloat)hSpacing
 | |
| {
 | |
|     return 8.f;
 | |
| }
 | |
| 
 | |
| - (CGFloat)vMargin
 | |
| {
 | |
|     return 4.f;
 | |
| }
 | |
| 
 | |
| - (CGSize)measureSizeWithMaxMessageWidth:(CGFloat)maxMessageWidth
 | |
| {
 | |
|     CGSize result = CGSizeZero;
 | |
| 
 | |
|     CGFloat labelsHeight = ([OWSGenericAttachmentView topLabelFont].lineHeight +
 | |
|         [OWSGenericAttachmentView bottomLabelFont].lineHeight + [OWSGenericAttachmentView labelVSpacing]);
 | |
|     CGFloat contentHeight = MAX(self.iconHeight, labelsHeight);
 | |
|     result.height = contentHeight + self.vMargin * 2;
 | |
| 
 | |
|     CGFloat labelsWidth
 | |
|         = MAX([self.topLabel sizeThatFits:CGSizeZero].width, [self.bottomLabel sizeThatFits:CGSizeZero].width);
 | |
|     CGFloat contentWidth = (self.iconWidth + labelsWidth + self.hSpacing);
 | |
|     result.width = MIN(maxMessageWidth, contentWidth + self.hMargin * 2);
 | |
| 
 | |
|     return CGSizeCeil(result);
 | |
| }
 | |
| 
 | |
| - (CGFloat)iconWidth
 | |
| {
 | |
|     return 36.f;
 | |
| }
 | |
| 
 | |
| - (CGFloat)iconHeight
 | |
| {
 | |
|     return 48.0f;
 | |
| }
 | |
| 
 | |
| - (void)createContentsWithConversationStyle:(ConversationStyle *)conversationStyle
 | |
| {
 | |
|     OWSAssertDebug(conversationStyle);
 | |
| 
 | |
|     self.axis = UILayoutConstraintAxisHorizontal;
 | |
|     self.alignment = UIStackViewAlignmentCenter;
 | |
|     self.spacing = self.hSpacing;
 | |
|     self.layoutMarginsRelativeArrangement = YES;
 | |
|     self.layoutMargins = UIEdgeInsetsMake(self.vMargin, 0, self.vMargin - 4, 0);
 | |
| 
 | |
|     // attachment_file
 | |
|     UIImage *image = [UIImage imageNamed:@"generic-attachment"];
 | |
|     OWSAssertDebug(image);
 | |
|     OWSAssertDebug(image.size.width == self.iconWidth);
 | |
|     OWSAssertDebug(image.size.height == self.iconHeight);
 | |
|     UIImageView *imageView = [UIImageView new];
 | |
|     imageView.image = image;
 | |
|     [self addArrangedSubview:imageView];
 | |
|     [imageView setContentHuggingHigh];
 | |
| 
 | |
|     NSString *_Nullable filename = self.attachment.sourceFilename;
 | |
|     if (!filename) {
 | |
|         filename = [[self.attachmentStream originalFilePath] lastPathComponent];
 | |
|     }
 | |
|     NSString *fileExtension = filename.pathExtension;
 | |
|     if (fileExtension.length < 1) {
 | |
|         fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType];
 | |
|     }
 | |
| 
 | |
|     UILabel *fileTypeLabel = [UILabel new];
 | |
|     fileTypeLabel.text = fileExtension.localizedUppercaseString;
 | |
|     fileTypeLabel.textColor = [UIColor ows_gray90Color];
 | |
|     fileTypeLabel.lineBreakMode = NSLineBreakByTruncatingTail;
 | |
|     fileTypeLabel.font = [UIFont ows_dynamicTypeCaption1Font].ows_mediumWeight;
 | |
|     fileTypeLabel.adjustsFontSizeToFitWidth = YES;
 | |
|     fileTypeLabel.textAlignment = NSTextAlignmentCenter;
 | |
|     // Center on icon.
 | |
|     [imageView addSubview:fileTypeLabel];
 | |
|     [fileTypeLabel autoCenterInSuperview];
 | |
|     [fileTypeLabel autoSetDimension:ALDimensionWidth toSize:self.iconWidth - 20.f];
 | |
| 
 | |
|     [self replaceIconWithDownloadProgressIfNecessary:imageView];
 | |
| 
 | |
|     UIStackView *labelsView = [UIStackView new];
 | |
|     labelsView.axis = UILayoutConstraintAxisVertical;
 | |
|     labelsView.spacing = [OWSGenericAttachmentView labelVSpacing];
 | |
|     labelsView.alignment = UIStackViewAlignmentLeading;
 | |
|     [self addArrangedSubview:labelsView];
 | |
| 
 | |
|     NSString *topText = [self.attachment.sourceFilename ows_stripped];
 | |
|     if (topText.length < 1) {
 | |
|         topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType].localizedUppercaseString;
 | |
|     }
 | |
|     if (topText.length < 1) {
 | |
|         topText = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments.");
 | |
|     }
 | |
|     UILabel *topLabel = [UILabel new];
 | |
|     self.topLabel = topLabel;
 | |
|     topLabel.text = topText;
 | |
|     topLabel.textColor = [conversationStyle bubbleTextColorWithIsIncoming:self.isIncoming];
 | |
|     topLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
 | |
|     topLabel.font = [OWSGenericAttachmentView topLabelFont];
 | |
|     [labelsView addArrangedSubview:topLabel];
 | |
| 
 | |
|     unsigned long long fileSize = 0;
 | |
|     if (self.attachmentStream) {
 | |
|         NSError *error;
 | |
|         fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:[self.attachmentStream originalFilePath]
 | |
|                                                                     error:&error]
 | |
|                        .fileSize;
 | |
|         OWSAssertDebug(!error);
 | |
|     }
 | |
|     // We don't want to show the file size while the attachment is downloading.
 | |
|     // To avoid layout jitter when the download completes, we reserve space in
 | |
|     // the layout using a whitespace string.
 | |
|     NSString *bottomText = @" ";
 | |
|     if (fileSize > 0) {
 | |
|         bottomText = [OWSFormat formatFileSize:fileSize];
 | |
|     }
 | |
|     UILabel *bottomLabel = [UILabel new];
 | |
|     self.bottomLabel = bottomLabel;
 | |
|     bottomLabel.text = bottomText;
 | |
|     bottomLabel.textColor = [conversationStyle bubbleSecondaryTextColorWithIsIncoming:self.isIncoming];
 | |
|     bottomLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
 | |
|     bottomLabel.font = [OWSGenericAttachmentView bottomLabelFont];
 | |
|     [labelsView addArrangedSubview:bottomLabel];
 | |
| }
 | |
| 
 | |
| - (void)replaceIconWithDownloadProgressIfNecessary:(UIView *)iconView
 | |
| {
 | |
|     if (!self.viewItem.attachmentPointer) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     switch (self.viewItem.attachmentPointer.state) {
 | |
|         case TSAttachmentPointerStateFailed:
 | |
|             // We don't need to handle the "tap to retry" state here,
 | |
|             // only download progress.
 | |
|             return;
 | |
|         case TSAttachmentPointerStateEnqueued:
 | |
|         case TSAttachmentPointerStateDownloading:
 | |
|             break;
 | |
|     }
 | |
|     switch (self.viewItem.attachmentPointer.pointerType) {
 | |
|         case TSAttachmentPointerTypeRestoring:
 | |
|             // TODO: Show "restoring" indicator and possibly progress.
 | |
|             return;
 | |
|         case TSAttachmentPointerTypeUnknown:
 | |
|         case TSAttachmentPointerTypeIncoming:
 | |
|             break;
 | |
|     }
 | |
|     NSString *_Nullable uniqueId = self.viewItem.attachmentPointer.uniqueId;
 | |
|     if (uniqueId.length < 1) {
 | |
|         OWSFailDebug(@"Missing uniqueId.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     CGSize iconViewSize = [iconView sizeThatFits:CGSizeZero];
 | |
|     CGFloat downloadViewSize = MIN(iconViewSize.width, iconViewSize.height);
 | |
|     MediaDownloadView *downloadView =
 | |
|         [[MediaDownloadView alloc] initWithAttachmentId:uniqueId radius:downloadViewSize * 0.5f];
 | |
|     iconView.layer.opacity = 0.01f;
 | |
|     [self addSubview:downloadView];
 | |
|     [downloadView autoSetDimensionsToSize:CGSizeMake(downloadViewSize, downloadViewSize)];
 | |
|     [downloadView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:iconView];
 | |
|     [downloadView autoAlignAxis:ALAxisVertical toSameAxisOfView:iconView];
 | |
| }
 | |
| 
 | |
| + (UIFont *)topLabelFont
 | |
| {
 | |
|     return [UIFont systemFontOfSize:LKValues.mediumFontSize];
 | |
| }
 | |
| 
 | |
| + (UIFont *)bottomLabelFont
 | |
| {
 | |
|     return [UIFont ows_dynamicTypeCaption1Font];
 | |
| }
 | |
| 
 | |
| + (CGFloat)labelVSpacing
 | |
| {
 | |
|     return 2.f;
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| NS_ASSUME_NONNULL_END
 |