mirror of https://github.com/oxen-io/session-ios
Merge branch 'charlesmchen/messageStateIndicators'
commit
612339670a
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSAttachmentStream;
|
||||
|
||||
typedef void (^AttachmentStateBlock)(BOOL isAttachmentReady);
|
||||
|
||||
// This entity is used by various attachment adapters to
|
||||
// coordinate view state with attachment uploads.
|
||||
// During attachment uploads we want to:
|
||||
//
|
||||
// * Dim the media view using a mask layer.
|
||||
// * Show and update a progress bar.
|
||||
// * Disable any media view controls using a callback.
|
||||
@interface AttachmentUploadView : NSObject
|
||||
|
||||
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
|
||||
superview:(UIView *)superview
|
||||
attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AttachmentUploadView.h"
|
||||
#import "OWSProgressView.h"
|
||||
#import "OWSUploadingService.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AttachmentUploadView ()
|
||||
|
||||
@property (nonatomic) TSAttachmentStream *attachment;
|
||||
|
||||
@property (nonatomic) OWSProgressView *progressView;
|
||||
|
||||
@property (nonatomic) CALayer *maskLayer;
|
||||
|
||||
@property (nonatomic) AttachmentStateBlock _Nullable attachmentStateCallback;
|
||||
|
||||
@property (nonatomic) BOOL isAttachmentReady;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AttachmentUploadView
|
||||
|
||||
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
|
||||
superview:(UIView *)superview
|
||||
attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
OWSAssert(attachment);
|
||||
OWSAssert(superview);
|
||||
|
||||
self.attachment = attachment;
|
||||
self.attachmentStateCallback = attachmentStateCallback;
|
||||
|
||||
_maskLayer = [CALayer layer];
|
||||
[_maskLayer setBackgroundColor:[UIColor blackColor].CGColor];
|
||||
[_maskLayer setOpacity:0.4f];
|
||||
[_maskLayer setFrame:superview.frame];
|
||||
[superview.layer addSublayer:_maskLayer];
|
||||
|
||||
const CGFloat progressWidth = round(superview.frame.size.width * 0.45f);
|
||||
const CGFloat progressHeight = round(progressWidth * 0.11f);
|
||||
CGRect progressFrame = CGRectMake(round((superview.frame.size.width - progressWidth) * 0.5f),
|
||||
round((superview.frame.size.height - progressHeight) * 0.5f),
|
||||
progressWidth,
|
||||
progressHeight);
|
||||
// The progress view is white. It will only be shown
|
||||
// while the mask layer is visible, so it will show up
|
||||
// even against all-white attachments.
|
||||
_progressView = [OWSProgressView new];
|
||||
_progressView.color = [UIColor whiteColor];
|
||||
_progressView.frame = progressFrame;
|
||||
[superview addSubview:_progressView];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(attachmentUploadProgress:)
|
||||
name:kAttachmentUploadProgressNotification
|
||||
object:nil];
|
||||
|
||||
_isAttachmentReady = self.attachment.isUploaded;
|
||||
|
||||
[self ensureViewState];
|
||||
|
||||
if (attachmentStateCallback) {
|
||||
self.attachmentStateCallback(_isAttachmentReady);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)setIsAttachmentReady:(BOOL)isAttachmentReady
|
||||
{
|
||||
if (_isAttachmentReady == isAttachmentReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isAttachmentReady = isAttachmentReady;
|
||||
|
||||
[self ensureViewState];
|
||||
|
||||
if (self.attachmentStateCallback) {
|
||||
self.attachmentStateCallback(isAttachmentReady);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ensureViewState
|
||||
{
|
||||
_maskLayer.hidden = self.isAttachmentReady;
|
||||
_progressView.hidden = self.isAttachmentReady;
|
||||
}
|
||||
|
||||
- (void)attachmentUploadProgress:(NSNotification *)notification
|
||||
{
|
||||
NSDictionary *userinfo = [notification userInfo];
|
||||
double progress = [[userinfo objectForKey:kAttachmentUploadProgressKey] doubleValue];
|
||||
NSString *attachmentID = [userinfo objectForKey:kAttachmentUploadAttachmentIDKey];
|
||||
if ([self.attachment.uniqueId isEqual:attachmentID]) {
|
||||
if (!isnan(progress)) {
|
||||
[_progressView setProgress:progress];
|
||||
self.isAttachmentReady = self.attachment.isUploaded;
|
||||
} else {
|
||||
OWSAssert(0);
|
||||
self.isAttachmentReady = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSProgressView : UIView
|
||||
|
||||
@property (nonatomic) UIColor *color;
|
||||
@property (nonatomic) CGFloat progress;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSProgressView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSProgressView ()
|
||||
|
||||
@property (nonatomic) CAShapeLayer *borderLayer;
|
||||
@property (nonatomic) CAShapeLayer *progressLayer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSProgressView
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initCommon];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initCommon];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initCommon
|
||||
{
|
||||
self.opaque = NO;
|
||||
self.userInteractionEnabled = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.color = [UIColor whiteColor];
|
||||
|
||||
self.borderLayer = [CAShapeLayer new];
|
||||
self.borderLayer.fillColor = self.color.CGColor;
|
||||
[self.layer addSublayer:self.borderLayer];
|
||||
|
||||
self.progressLayer = [CAShapeLayer new];
|
||||
self.progressLayer.fillColor = self.color.CGColor;
|
||||
[self.layer addSublayer:self.progressLayer];
|
||||
|
||||
[self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
|
||||
[self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
{
|
||||
if (_progress != progress) {
|
||||
_progress = progress;
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setColor:(UIColor *)color
|
||||
{
|
||||
if (![_color isEqual:color]) {
|
||||
_color = color;
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
CGFloat kBorderThickness = self.bounds.size.height * 0.15f;
|
||||
CGFloat kOuterRadius = self.bounds.size.height * 0.3f;
|
||||
CGFloat kInnerRadius = kOuterRadius - kBorderThickness;
|
||||
// We want to slightly overlap the border with the progress
|
||||
// to achieve a clean effect.
|
||||
CGFloat kProgressInset = kBorderThickness - 0.5f;
|
||||
|
||||
UIBezierPath *borderPath = [UIBezierPath new];
|
||||
|
||||
// Add the outer border.
|
||||
[borderPath appendPath:[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:kOuterRadius]];
|
||||
[borderPath
|
||||
appendPath:[UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, kBorderThickness, kBorderThickness)
|
||||
cornerRadius:kInnerRadius]];
|
||||
|
||||
self.borderLayer.path = borderPath.CGPath;
|
||||
self.borderLayer.fillColor = self.color.CGColor;
|
||||
self.borderLayer.fillRule = kCAFillRuleEvenOdd;
|
||||
|
||||
UIBezierPath *progressPath = [UIBezierPath new];
|
||||
|
||||
// Add the inner progress.
|
||||
CGRect progressRect = CGRectInset(self.bounds, kProgressInset, kProgressInset);
|
||||
progressRect.size.width *= MAX(0.f, MIN(1.f, self.progress));
|
||||
[progressPath appendPath:[UIBezierPath bezierPathWithRect:progressRect]];
|
||||
|
||||
self.progressLayer.path = progressPath.CGPath;
|
||||
self.progressLayer.fillColor = self.color.CGColor;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
{
|
||||
return CGSizeMake(150, 16);
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize
|
||||
{
|
||||
return CGSizeMake(UIViewNoIntrinsicMetric, 16);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Loading…
Reference in New Issue