1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-11 08:59:48 +01:00
Telegram/Watch/Extension/TGNeoRowController.m
2016-02-25 01:03:51 +01:00

455 lines
16 KiB
Objective-C

#import "TGNeoRowController.h"
#import "TGNeoConversationRowController.h"
#import "TGNeoConversationSimpleRowController.h"
#import "TGNeoConversationMediaRowController.h"
#import "TGNeoConversationStaticRowController.h"
#import "TGStringUtils.h"
#import "TGLocationUtils.h"
#import "TGNeoMessageViewModel.h"
#import "TGNeoBubbleMessageViewModel.h"
#import "TGNeoStickerMessageViewModel.h"
#import "TGBridgeMessage.h"
#import "WKInterfaceGroup+Signals.h"
#import "TGBridgeMediaSignals.h"
@interface TGNeoRowController ()
{
TGNeoMessageViewModel *_viewModel;
SMetaDisposable *_renderDisposable;
bool _pendingRendering;
bool _processing;
NSString *_normalIconName;
NSString *_processingIconName;
}
@end
@implementation TGNeoRowController
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_renderDisposable = [[SMetaDisposable alloc] init];
}
return self;
}
- (void)dealloc
{
[_renderDisposable dispose];
}
+ (CGSize)containerSizeForMessage:(TGBridgeMessage *)message
{
if (message.outgoing)
{
static dispatch_once_t onceToken;
static CGSize containerSize;
dispatch_once(&onceToken, ^
{
CGSize screenSize = [[WKInterfaceDevice currentDevice] screenBounds].size;
containerSize = CGSizeMake(screenSize.width - 5, screenSize.height);
});
return containerSize;
}
else
{
static dispatch_once_t onceToken;
static CGSize containerSize;
dispatch_once(&onceToken, ^
{
containerSize = [[WKInterfaceDevice currentDevice] screenBounds].size;
});
return containerSize;
}
}
- (void)updateWithMessage:(TGBridgeMessage *)message context:(TGBridgeContext *)context index:(NSInteger)index type:(TGNeoMessageType)type
{
bool isChannelMessage = (type == TGNeoMessageTypeChannel);
if (!isChannelMessage)
[self updateStatusWithMessage:message];
if ([self renderIfNeeded])
return;
if (_viewModel != nil)
return;
_viewModel = [TGNeoMessageViewModel viewModelForMessage:message type:type context:context additionalPeers:self.additionalPeers];
CGSize containerSize = [TGNeoRowController containerSizeForMessage:message];
CGSize contentSize = [_viewModel layoutWithContainerSize:containerSize];
if (_viewModel.showBubble)
{
if (isChannelMessage)
{
[self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleChannel"];
}
else
{
if (message.outgoing)
[self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleOutgoing"];
else
[self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleIncoming"];
}
}
if (!isChannelMessage && message.outgoing)
{
[self.bubbleGroup setHorizontalAlignment:WKInterfaceObjectHorizontalAlignmentRight];
if (!_viewModel.showBubble)
[self.statusGroup setContentInset:UIEdgeInsetsMake(4, 0, 0, 0)];
}
self.bubbleGroup.width = contentSize.width;
self.bubbleGroup.height = contentSize.height;
self.contentGroup.width = contentSize.width;
self.contentGroup.height = contentSize.height;
[self applyAdditionalLayoutForViewModel:_viewModel];
bool shouldRender = true;
if (self.shouldRenderContent != nil)
shouldRender = self.shouldRenderContent();
if (shouldRender)
[self _render];
else
_pendingRendering = true;
}
- (void)_render
{
_pendingRendering = false;
bool onMainThread = true;
SSignal *signal = [TGNeoRenderableViewModel renderSignalForViewModel:_viewModel];
if (!onMainThread)
signal = [[signal startOn:[SQueue concurrentDefaultQueue]] deliverOn:[SQueue mainQueue]];
__weak TGNeoRowController *weakSelf = self;
[_renderDisposable setDisposable:[signal startWithNext:^(UIImage *image)
{
__strong TGNeoRowController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf.contentGroup setBackgroundImage:image];
}]];
}
- (bool)renderIfNeeded
{
if (!_pendingRendering)
return false;
[self _render];
return true;
}
- (void)updateStatusWithMessage:(TGBridgeMessage *)message
{
if (message.outgoing)
{
bool failed = (message.deliveryState == TGBridgeMessageDeliveryStateFailed);
bool unread = (message.deliveryState == TGBridgeMessageDeliveryStatePending || (message.deliveryState == TGBridgeMessageDeliveryStateDelivered && message.unread));
bool dotHidden = !failed && !unread;
self.statusGroup.hidden = dotHidden;
if (!dotHidden)
{
if (failed)
[self.statusIcon setTintColor:[UIColor hexColor:0xff4a5c]];
else if (unread)
[self.statusIcon setTintColor:[UIColor hexColor:0x2ba2e7]];
}
}
}
- (void)applyAdditionalLayoutForViewModel:(TGNeoMessageViewModel *)viewModel
{
NSDictionary *layout = viewModel.additionalLayout;
NSDictionary *headerGroupLayout = layout[TGNeoMessageHeaderGroup];
if (headerGroupLayout != nil)
{
self.headerWrapperGroup.hidden = false;
NSDictionary *imageLayout = headerGroupLayout[TGNeoMessageReplyImageGroup];
if (imageLayout != nil)
{
TGBridgeMediaAttachment *attachment = imageLayout[TGNeoMessageReplyMediaAttachment];
if (attachment != nil)
{
CGSize imageSize = CGSizeMake(26, 26);
SSignal *imageSignal = [attachment isKindOfClass:[TGBridgeImageMediaAttachment class]] ? [TGBridgeMediaSignals previewWithImageAttachment:(TGBridgeImageMediaAttachment *)attachment size:imageSize] : [TGBridgeMediaSignals previewWithVideoAttachment:(TGBridgeVideoMediaAttachment *)attachment size:imageSize];
[self.replyImageGroup setBackgroundImageSignal:imageSignal isVisible:self.isVisible];
}
}
NSValue *insetValue = headerGroupLayout[TGNeoContentInset];
if (insetValue != nil)
{
UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
[self.headerWrapperGroup setContentInset:inset];
}
}
NSDictionary *mediaGroupLayout = layout[TGNeoMessageMediaGroup];
if (mediaGroupLayout != nil)
{
self.mediaWrapperGroup.hidden = false;
if (![viewModel isKindOfClass:[TGNeoStickerMessageViewModel class]])
{
if (!viewModel.showBubble)
[self.imageGroup setCornerRadius:13];
else
[self.imageGroup setCornerRadius:12];
}
NSDictionary *imageGroup = mediaGroupLayout[TGNeoMessageMediaImage];
NSDictionary *mapGroup = mediaGroupLayout[TGNeoMessageMediaMap];
if (imageGroup != nil)
{
self.imageGroup.hidden = false;
TGBridgeMediaAttachment *attachment = imageGroup[TGNeoMessageMediaImageAttachment];
CGSize size = CGSizeMake(100, 100);
NSValue *imageSizeValue = imageGroup[TGNeoMessageMediaSize];
if (imageSizeValue != nil)
{
size = imageSizeValue.CGSizeValue;
self.imageGroup.width = size.width;
self.imageGroup.height = size.height;
}
bool hasPlayButton = [imageGroup[TGNeoMessageMediaPlayButton] boolValue];
bool hasSpinner = [imageGroup[TGNeoMessageMediaImageSpinner] boolValue];
if (hasSpinner)
self.spinnerImage.hidden = false;
__weak TGNeoRowController *weakSelf = self;
if ([attachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
{
[self.imageGroup setBackgroundImageSignal:[[TGBridgeMediaSignals previewWithImageAttachment:(TGBridgeImageMediaAttachment *)attachment size:size] onNext:^(id next)
{
__strong TGNeoRowController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf.spinnerImage.hidden = true;
}] isVisible:self.isVisible];
}
else if ([attachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
{
[self.imageGroup setBackgroundImageSignal:[[TGBridgeMediaSignals previewWithVideoAttachment:(TGBridgeVideoMediaAttachment *)attachment size:size] onNext:^(id next)
{
__strong TGNeoRowController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf.spinnerImage.hidden = true;
if (hasPlayButton)
strongSelf.videoButton.hidden = false;
}] isVisible:self.isVisible];
}
else if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
{
[self.imageGroup setBackgroundImageSignal:[TGBridgeMediaSignals stickerWithDocumentAttachment:(TGBridgeDocumentMediaAttachment *)attachment type:TGMediaStickerImageTypeNormal] isVisible:self.isVisible];
}
}
else if (mapGroup != nil)
{
self.map.hidden = false;
CGSize size = CGSizeMake(100, 100);
NSValue *imageSizeValue = mapGroup[TGNeoMessageMediaSize];
if (imageSizeValue != nil)
{
size = imageSizeValue.CGSizeValue;
self.map.width = size.width;
self.map.height = size.height;
}
NSValue *coordinateValue = mapGroup[TGNeoMessageMediaMapCoordinate];
if (coordinateValue != nil)
{
CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue];
CLLocationCoordinate2D regionCoordinate = CLLocationCoordinate2DMake([TGLocationUtils adjustGMapLatitude:coordinate.latitude withPixelOffset:-10 zoom:15], coordinate.longitude);
self.map.region = MKCoordinateRegionMake(regionCoordinate, MKCoordinateSpanMake(0.003, 0.003));
self.map.centerPinCoordinate = coordinate;
}
}
NSValue *insetValue = mediaGroupLayout[TGNeoContentInset];
if (insetValue != nil)
{
UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
[self.mediaWrapperGroup setContentInset:inset];
}
}
NSDictionary *metaGroupLayout = layout[TGNeoMessageMetaGroup];
if (metaGroupLayout != nil)
{
self.metaWrapperGroup.hidden = false;
NSDictionary *audioLayout = metaGroupLayout[TGNeoMessageAudioButton];
if (audioLayout != nil)
{
self.audioButton.hidden = false;
NSNumber *hasBackground = audioLayout[TGNeoMessageAudioButtonHasBackground] ?: @true;
if (hasBackground.boolValue)
[self.audioButtonGroup setBackgroundColor:[UIColor hexColor:0x6bbeee]];
else
[self.audioButtonGroup setBackgroundColor:[UIColor clearColor]];
NSString *audioIcon = audioLayout[TGNeoMessageAudioIcon];
if (audioIcon != nil)
{
_normalIconName = audioIcon;
[self.audioIcon setImageNamed:audioIcon];
}
NSString *audioAnimatedIcon = audioLayout[TGNeoMessageAudioAnimatedIcon];
if (audioAnimatedIcon != nil)
_processingIconName = audioAnimatedIcon;
UIColor *iconColor = audioLayout[TGNeoMessageAudioIconTint];
if (iconColor != nil)
[self.audioIcon setTintColor:iconColor];
}
NSDictionary *avatarLayout = metaGroupLayout[TGNeoMessageAvatarGroup];
if (avatarLayout != nil)
{
self.avatarGroup.hidden = false;
NSString *avatarUrl = avatarLayout[TGNeoMessageAvatarUrl];
if (avatarUrl.length > 0)
{
[self.avatarGroup setBackgroundImageSignal:[TGBridgeMediaSignals avatarWithUrl:avatarUrl type:TGBridgeMediaAvatarTypeSmall] isVisible:self.isVisible];
}
else
{
NSString *initials = avatarLayout[TGNeoMessageAvatarInitials];
UIColor *color = avatarLayout[TGNeoMessageAvatarColor];
self.avatarLabel.hidden = false;
self.avatarLabel.text = initials;
[self.avatarGroup setBackgroundColor:color];
}
}
NSValue *insetValue = metaGroupLayout[TGNeoContentInset];
if (insetValue != nil)
{
UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
[self.metaWrapperGroup setContentInset:inset];
}
}
}
- (IBAction)remotePressedAction
{
if (self.buttonPressed != nil)
self.buttonPressed();
}
- (void)setProcessingState:(bool)processing
{
if (processing == _processing)
return;
_processing = processing;
if (processing)
{
[self.audioIcon setImageNamed:_processingIconName];
[self.audioIcon startAnimatingWithImagesInRange:NSMakeRange(0, 39) duration:0.65 repeatCount:0];
}
else
{
[self.audioIcon stopAnimating];
[self.audioIcon setImageNamed:_normalIconName];
}
}
+ (Class)rowControllerClassForMessage:(TGBridgeMessage *)message
{
Class class = [TGNeoConversationRowController class];
bool hasReplyHeader = false;
bool hasForwardHeader = false;
bool hasAttachments = false;
for (TGBridgeMediaAttachment *attachment in message.media)
{
if ([attachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
{
hasAttachments = true;
class = [TGNeoConversationMediaRowController class];
}
else if ([attachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
{
hasAttachments = true;
class = [TGNeoConversationMediaRowController class];
}
else if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
{
hasAttachments = true;
TGBridgeDocumentMediaAttachment *documentAttachment = (TGBridgeDocumentMediaAttachment *)attachment;
if (documentAttachment.isSticker)
class = [TGNeoConversationMediaRowController class];
}
else if ([attachment isKindOfClass:[TGBridgeLocationMediaAttachment class]])
{
hasAttachments = true;
if (((TGBridgeLocationMediaAttachment *)attachment).venue == nil)
class = [TGNeoConversationMediaRowController class];
}
else if ([attachment isKindOfClass:[TGBridgeAudioMediaAttachment class]])
{
hasAttachments = true;
}
else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
{
class = [TGNeoConversationStaticRowController class];
}
else if ([attachment isKindOfClass:[TGBridgeForwardedMessageMediaAttachment class]])
{
hasForwardHeader = true;
}
else if ([attachment isKindOfClass:[TGBridgeReplyMessageMediaAttachment class]])
{
hasReplyHeader = true;
}
}
if (class == [TGNeoConversationRowController class] && !hasReplyHeader && !hasAttachments)
class = [TGNeoConversationSimpleRowController class];
return class;
}
@end