mirror of
https://github.com/danog/Telegram.git
synced 2024-11-30 04:29:15 +01:00
923 lines
37 KiB
Objective-C
923 lines
37 KiB
Objective-C
#import "ShareViewController.h"
|
|
|
|
#import <SSignalKit/SSignalKit.h>
|
|
|
|
#import <MobileCoreServices/MobileCoreServices.h>
|
|
#import <AVFoundation/AVFoundation.h>
|
|
|
|
#import "ApiLayer38.h"
|
|
|
|
#import "TGShareContextSignal.h"
|
|
#import "TGChatListSignal.h"
|
|
#import "TGSearchSignals.h"
|
|
#import "TGSendMessageSignals.h"
|
|
#import "TGUploadMediaSignals.h"
|
|
|
|
#import "TGShareChatListCell.h"
|
|
|
|
#import "TGChatModel.h"
|
|
#import "TGPrivateChatModel.h"
|
|
#import "TGGroupChatModel.h"
|
|
#import "TGUserModel.h"
|
|
|
|
#import "TGColor.h"
|
|
#import "TGGeometry.h"
|
|
#import "TGScaleImage.h"
|
|
#import "TGUploadedMessageContentText.h"
|
|
#import "TGUploadedMessageContentMedia.h"
|
|
|
|
#import "TGProgressAlert.h"
|
|
|
|
#import "TGSharePasscodeView.h"
|
|
#import "TGMimeTypeMap.h"
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
@interface ShareViewController () <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>
|
|
{
|
|
TGShareContext *_shareContext;
|
|
|
|
NSArray *_chatModels;
|
|
NSArray *_userModels;
|
|
|
|
NSArray *_searchSections;
|
|
|
|
UIActivityIndicatorView *_activityIndicator;
|
|
|
|
UITableView *_tableView;
|
|
UITableView *_searchResultsTableView;
|
|
|
|
UISearchBar *_searchBar;
|
|
UIView *_searchDimView;
|
|
|
|
UINavigationBar *_navigationBar;
|
|
|
|
TGProgressAlert *_progressAlert;
|
|
TGSharePasscodeView *_passcodeView;
|
|
|
|
id<SDisposable> _shareContextDisposable;
|
|
SMetaDisposable *_chatListDisposable;
|
|
SMetaDisposable *_searchDisposable;
|
|
SMetaDisposable *_sendMessagesDisposable;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation ShareViewController
|
|
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
|
{
|
|
self = [super initWithCoder:aDecoder];
|
|
if (self != nil)
|
|
{
|
|
self.title = NSLocalizedString(@"Share.Title", nil);
|
|
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Share.Cancel", nil) style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_shareContextDisposable dispose];
|
|
[_chatListDisposable dispose];
|
|
[_searchDisposable dispose];
|
|
[_sendMessagesDisposable dispose];
|
|
}
|
|
|
|
- (void)animateDismiss
|
|
{
|
|
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
|
|
animation.fromValue = [NSValue valueWithCGPoint:self.view.layer.position];
|
|
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.layer.position.x, self.view.layer.position.y + self.view.frame.size.height)];
|
|
animation.duration = 0.3;
|
|
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
|
animation.removedOnCompletion = true;
|
|
animation.fillMode = kCAFillModeForwards;
|
|
[self.view.layer addAnimation:animation forKey:@"position"];
|
|
self.view.layer.position = CGPointMake(self.view.layer.position.x, self.view.layer.position.y + self.view.frame.size.height);
|
|
}
|
|
|
|
- (void)cancelPressed
|
|
{
|
|
[self animateDismiss];
|
|
|
|
self.view.userInteractionEnabled = false;
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
|
|
{
|
|
[self cancel];
|
|
});
|
|
}
|
|
|
|
- (UITextField *)findTextField:(UIView *)view
|
|
{
|
|
if ([view isKindOfClass:[UITextField class]])
|
|
return (UITextField *)view;
|
|
|
|
for (UIView *subview in view.subviews)
|
|
{
|
|
UITextField *result = [self findTextField:subview];
|
|
if (result != nil)
|
|
return result;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
static CGRect UISearchBarTextField_editingRectForBounds(__unused id self, __unused SEL cmd, CGRect bounds)
|
|
{
|
|
bounds.origin.x += 28.0f;
|
|
bounds.size.width -= 10.0f;
|
|
return bounds;
|
|
}
|
|
|
|
- (void)loadView
|
|
{
|
|
[super loadView];
|
|
|
|
for (UIView *subview in self.view.subviews.copy)
|
|
{
|
|
[subview removeFromSuperview];
|
|
}
|
|
|
|
for (CALayer *layer in self.view.layer.sublayers.copy)
|
|
{
|
|
[layer removeFromSuperlayer];
|
|
}
|
|
|
|
self.view.backgroundColor = [UIColor clearColor];
|
|
|
|
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
|
|
[_tableView setContentInset:UIEdgeInsetsMake(20.0f + 44.0f, 0.0f, 0.0f, 0.0f)];
|
|
[_tableView setScrollIndicatorInsets:UIEdgeInsetsMake(20.0f + 44.0f, 0.0f, 0.0f, 0.0f)];
|
|
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_tableView.dataSource = self;
|
|
_tableView.delegate = self;
|
|
_tableView.rowHeight = 47.0f;
|
|
_tableView.separatorInset = UIEdgeInsetsMake(0.0f, 65.0f, 0.0f, 0.0f);
|
|
_tableView.tableFooterView = [[UIView alloc] init];
|
|
[self.view addSubview:_tableView];
|
|
|
|
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _tableView.frame.size.width, 44.0f)];
|
|
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
[_searchBar setPlaceholder:NSLocalizedString(@"Share.Search", nil)];
|
|
_searchBar.delegate = self;
|
|
|
|
static UIImage *searchBarBackgroundImage = nil;
|
|
static UIImage *searchFieldBackgroundImage = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
{
|
|
CGFloat radius = 4.0f;
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(radius * 2.0f + 2.0f, 28.0f), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
CGContextSetFillColorWithColor(context, TGColorWithHex(0xededed).CGColor);
|
|
CGContextFillRect(context, CGRectMake(radius, 0.0f, 2.0f, 28.0f));
|
|
CGContextFillRect(context, CGRectMake(0.0f, radius, radius * 2.0f + 2.0f, 28.0f - radius * 2.0f));
|
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, radius * 2.0f, radius * 2.0f));
|
|
CGContextFillEllipseInRect(context, CGRectMake(radius * 2.0f + 2.0f - radius * 2.0f, 0.0f, radius * 2.0f, radius * 2.0f));
|
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 28.0f - radius * 2.0f, radius * 2.0f, radius * 2.0f));
|
|
CGContextFillEllipseInRect(context, CGRectMake(radius * 2.0f + 2.0f - radius * 2.0f, 28.0f - radius * 2.0f, radius * 2.0f, radius * 2.0f));
|
|
searchFieldBackgroundImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
}
|
|
{
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(2.0f, 44.0f), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
|
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 2.0f, 44.0f));
|
|
searchBarBackgroundImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
}
|
|
});
|
|
[_searchBar setBackgroundImage:searchBarBackgroundImage forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
|
|
[_searchBar setSearchFieldBackgroundImage:searchFieldBackgroundImage forState:UIControlStateNormal];
|
|
UITextField *searchTextField = [self findTextField:_searchBar];
|
|
if (searchTextField != nil)
|
|
{
|
|
Class newClass = objc_allocateClassPair([searchTextField class], "UISearchBarTextFieldWithInset", 0);
|
|
Method method_editingRectForBounds = class_getInstanceMethod([UITextField class], @selector(editingRectForBounds:));
|
|
if (method_editingRectForBounds != NULL)
|
|
{
|
|
if (!class_addMethod(newClass, @selector(editingRectForBounds:), (IMP)&UISearchBarTextField_editingRectForBounds, method_getTypeEncoding(method_editingRectForBounds)))
|
|
{
|
|
NSLog(@"failed to swizzle");
|
|
}
|
|
}
|
|
object_setClass(searchTextField, newClass);
|
|
}
|
|
|
|
_tableView.tableHeaderView = _searchBar;
|
|
|
|
_searchDimView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 20.0f + 44.0f + 44.0f, self.view.frame.size.width, self.view.frame.size.height + 44.0f)];
|
|
_searchDimView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
|
|
_searchDimView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
[_searchDimView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(searchDimViewTapped:)]];
|
|
_searchDimView.alpha = 0.0f;
|
|
[self.view addSubview:_searchDimView];
|
|
|
|
_searchResultsTableView = [[UITableView alloc] initWithFrame:_searchDimView.frame style:UITableViewStylePlain];
|
|
[_searchResultsTableView setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f)];
|
|
_searchResultsTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_searchResultsTableView.dataSource = self;
|
|
_searchResultsTableView.delegate = self;
|
|
_searchResultsTableView.rowHeight = 47.0f;
|
|
_searchResultsTableView.separatorInset = UIEdgeInsetsMake(0.0f, 65.0f, 0.0f, 0.0f);
|
|
_searchResultsTableView.tableFooterView = [[UIView alloc] init];
|
|
_searchResultsTableView.hidden = true;
|
|
[self.view addSubview:_searchResultsTableView];
|
|
|
|
if (_chatModels == nil)
|
|
{
|
|
_tableView.userInteractionEnabled = false;
|
|
_searchBar.hidden = true;
|
|
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
|
_activityIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
|
|
_activityIndicator.frame = CGRectMake((CGFloat)floor((self.view.frame.size.width - _activityIndicator.frame.size.width) / 2.0f), (CGFloat)floor((self.view.frame.size.height - _activityIndicator.frame.size.height) / 2.0f), _activityIndicator.frame.size.width, _activityIndicator.frame.size.height);
|
|
[self.view addSubview:_activityIndicator];
|
|
[_activityIndicator startAnimating];
|
|
}
|
|
|
|
_navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 20.0f + 44.0f)];
|
|
_navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
[_navigationBar pushNavigationItem:self.navigationItem animated:false];
|
|
[self.view addSubview:_navigationBar];
|
|
|
|
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
|
|
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(self.view.layer.position.x, self.view.layer.position.y + self.view.frame.size.height)];
|
|
animation.toValue = [NSValue valueWithCGPoint:self.view.layer.position];
|
|
animation.duration = 0.3;
|
|
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
|
|
animation.removedOnCompletion = true;
|
|
animation.fillMode = kCAFillModeForwards;
|
|
[self.view.layer addAnimation:animation forKey:@"position"];
|
|
|
|
__weak ShareViewController *weakSelf = self;
|
|
[_shareContextDisposable dispose];
|
|
_shareContextDisposable = [[[TGShareContextSignal shareContext] deliverOn:[SQueue mainQueue]] startWithNext:^(id next)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
if ([next isKindOfClass:[TGShareContext class]])
|
|
{
|
|
if (strongSelf->_passcodeView != nil)
|
|
{
|
|
UIView *passcodeView = strongSelf->_passcodeView;
|
|
strongSelf->_passcodeView = nil;
|
|
|
|
[UIView animateWithDuration:0.2 delay:0.0 options:0 animations:^
|
|
{
|
|
passcodeView.frame = CGRectOffset(passcodeView.frame, 0.0f, passcodeView.frame.size.height);
|
|
} completion:^(BOOL finished)
|
|
{
|
|
[passcodeView removeFromSuperview];
|
|
}];
|
|
}
|
|
|
|
[strongSelf setShareContext:next];
|
|
}
|
|
else if ([next isKindOfClass:[TGEncryptedShareContext class]])
|
|
{
|
|
TGEncryptedShareContext *encryptedShareContext = next;
|
|
|
|
if (strongSelf->_passcodeView == nil)
|
|
{
|
|
strongSelf->_passcodeView = [[TGSharePasscodeView alloc] initWithSimpleMode:encryptedShareContext.simplePassword cancel:^
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf cancelPressed];
|
|
} verify:^(NSString *passcode, void (^result)(bool))
|
|
{
|
|
result(encryptedShareContext.verifyPassword(passcode));
|
|
} alertPresentationController:self];
|
|
|
|
strongSelf->_passcodeView.frame = strongSelf.view.bounds;
|
|
strongSelf->_passcodeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
[strongSelf.view addSubview:strongSelf->_passcodeView];
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf->_passcodeView showKeyboard];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
|
|
{
|
|
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
|
|
}
|
|
|
|
- (void)setShareContext:(TGShareContext *)shareContext
|
|
{
|
|
_shareContext = shareContext;
|
|
|
|
_chatListDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
__weak ShareViewController *weakSelf = self;
|
|
[_chatListDisposable setDisposable:[[[TGChatListSignal remoteChatListWithContext:_shareContext] deliverOn:[SQueue mainQueue]] startWithNext:^(NSDictionary *next)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
_searchBar.hidden = false;
|
|
[_activityIndicator stopAnimating];
|
|
[_activityIndicator removeFromSuperview];
|
|
_tableView.userInteractionEnabled = true;
|
|
|
|
[strongSelf setChatModels:next[@"chats"] userModels:next[@"users"]];
|
|
}
|
|
}]];
|
|
}
|
|
|
|
- (void)setChatModels:(NSArray *)chatModels userModels:(NSArray *)userModels
|
|
{
|
|
_chatModels = chatModels;
|
|
_userModels = userModels;
|
|
|
|
[_tableView reloadData];
|
|
}
|
|
|
|
- (void)setSearchResultsSections:(NSArray *)sections
|
|
{
|
|
_searchSections = sections;
|
|
[_searchResultsTableView reloadData];
|
|
}
|
|
|
|
- (BOOL)isContentValid
|
|
{
|
|
return true;
|
|
}
|
|
|
|
- (NSArray *)configurationItems
|
|
{
|
|
return @[];
|
|
}
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
|
{
|
|
if (tableView == _tableView)
|
|
return 1;
|
|
else if (tableView == _searchResultsTableView)
|
|
return _searchSections.count;
|
|
return 0;
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
{
|
|
if (tableView == _tableView)
|
|
return _chatModels.count;
|
|
else if (tableView == _searchResultsTableView)
|
|
return [(NSArray *)(_searchSections[section][@"chats"]) count];
|
|
return 0;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
TGShareChatListCell *cell = (TGShareChatListCell *)[tableView dequeueReusableCellWithIdentifier:@"TGShareChatListCell"];
|
|
if (cell == nil)
|
|
{
|
|
cell = [[TGShareChatListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TGShareChatListCell"];
|
|
}
|
|
|
|
if (tableView == _tableView)
|
|
[cell setChatModel:_chatModels[indexPath.row] associatedUsers:_userModels shareContext:_shareContext];
|
|
else if (tableView == _searchResultsTableView)
|
|
{
|
|
[cell setChatModel:_searchSections[indexPath.section][@"chats"][indexPath.row] associatedUsers:_searchSections[indexPath.section][@"users"] shareContext:_shareContext];
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
[tableView deselectRowAtIndexPath:indexPath animated:true];
|
|
|
|
TGChatModel *selectedModel = nil;
|
|
NSArray *selectedModelUsers = nil;
|
|
|
|
if (tableView == _tableView)
|
|
{
|
|
selectedModel = _chatModels[indexPath.row];
|
|
selectedModelUsers = _userModels;
|
|
}
|
|
else if (tableView == _searchResultsTableView)
|
|
{
|
|
selectedModel = _searchSections[indexPath.section][@"chats"][indexPath.row];
|
|
selectedModelUsers = _searchSections[indexPath.section][@"users"];
|
|
}
|
|
|
|
if (selectedModel != nil)
|
|
{
|
|
NSString *format = @"";
|
|
NSString *title = @"";
|
|
if ([selectedModel isKindOfClass:[TGPrivateChatModel class]])
|
|
{
|
|
format = NSLocalizedString(@"Share.ShareWithPerson", nil);
|
|
for (TGUserModel *user in selectedModelUsers)
|
|
{
|
|
if (user.userId == selectedModel.peerId.peerId)
|
|
{
|
|
title = user.displayName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ([selectedModel isKindOfClass:[TGGroupChatModel class]])
|
|
{
|
|
format = NSLocalizedString(@"Share.ShareWithGroup", nil);
|
|
title = ((TGGroupChatModel *)selectedModel).title;
|
|
}
|
|
|
|
NSString *message = [[NSString alloc] initWithFormat:format, title];
|
|
|
|
__weak ShareViewController *weakSelf = self;
|
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
|
|
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Share.Cancel", nil) style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction *action)
|
|
{
|
|
|
|
}]];
|
|
TGPeerId peerId = selectedModel.peerId;
|
|
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Share.OK", nil) style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf sendItemsToPeerWithId:peerId users:selectedModelUsers];
|
|
}]];
|
|
[self presentViewController:alertController animated:true completion:nil];
|
|
}
|
|
}
|
|
|
|
- (void)sendItemsToPeerWithId:(TGPeerId)peerId users:(NSArray *)users
|
|
{
|
|
__weak ShareViewController *weakSelf = self;
|
|
|
|
|
|
NSMutableArray *providers = [[NSMutableArray alloc] init];
|
|
|
|
for (NSExtensionItem *item in self.extensionContext.inputItems)
|
|
{
|
|
for (NSItemProvider *provider in item.attachments)
|
|
{
|
|
if ([provider hasItemConformingToTypeIdentifier:@"public.file-url"])
|
|
[providers addObject:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage])
|
|
[providers addObject:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL])
|
|
[providers addObject:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:@"public.text"])
|
|
[providers addObject:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeAudio])
|
|
[providers addObject:provider];
|
|
}
|
|
}
|
|
|
|
SSignal *itemsSignal = [SSignal complete];
|
|
|
|
NSInteger providerIndex = -1;
|
|
for (NSItemProvider *provider in providers)
|
|
{
|
|
providerIndex++;
|
|
|
|
SSignal *dataSignal = nil;
|
|
if ([provider hasItemConformingToTypeIdentifier:@"public.file-url"])
|
|
{
|
|
SSignal *urlSignal = [self signalForUrlItemProvider:provider];
|
|
dataSignal = [urlSignal mapToSignal:^SSignal *(NSURL *url)
|
|
{
|
|
NSData *data = [[NSData alloc] initWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:nil];
|
|
if (data == nil)
|
|
return [SSignal fail:nil];
|
|
NSString *fileName = [[url pathComponents] lastObject];
|
|
if (fileName.length == 0)
|
|
fileName = @"file.bin";
|
|
NSString *extension = [fileName pathExtension];
|
|
NSString *mimeType = [TGMimeTypeMap mimeTypeForExtension:[extension lowercaseString]];
|
|
if (mimeType == nil)
|
|
mimeType = @"application/octet-stream";
|
|
return [SSignal single:@{@"data": data, @"fileName": fileName, @"mimeType": mimeType}];
|
|
}];
|
|
}
|
|
else if ([provider hasItemConformingToTypeIdentifier:@"public.text"])
|
|
dataSignal = [self signalForTextItemProvider:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage])
|
|
dataSignal = [self signalForImageItemProvider:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL])
|
|
dataSignal = [self signalForTextUrlItemProvider:provider];
|
|
else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeAudio])
|
|
dataSignal = [self signalForDataItemProvider:provider];
|
|
|
|
if (dataSignal != nil)
|
|
{
|
|
SSignal *sendMessageSignal = nil;
|
|
|
|
sendMessageSignal = [dataSignal mapToSignal:^SSignal *(id next)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
SSignal *uploadMediaSignal = nil;
|
|
|
|
if ([next respondsToSelector:@selector(objectForKey:)] && [next[@"image"] isKindOfClass:[UIImage class]])
|
|
{
|
|
UIImage *image = next[@"image"];
|
|
if (image != nil)
|
|
{
|
|
image = TGScaleImage(image, TGFitSize(CGSizeMake(image.size.width * image.scale, image.size.height * image.scale), CGSizeMake(1280.0f, 1280.0f)));
|
|
NSData *imageData = UIImageJPEGRepresentation(image, 0.54f);
|
|
uploadMediaSignal = [TGUploadMediaSignals uploadPhotoWithContext:strongSelf->_shareContext data:imageData];
|
|
}
|
|
}
|
|
else if ([next respondsToSelector:@selector(objectForKey:)])
|
|
{
|
|
NSData *data = next[@"data"];
|
|
|
|
UIImage *image = [[UIImage alloc] initWithData:data];
|
|
if (image != nil)
|
|
{
|
|
bool isGif = false;
|
|
if (data.length > 4)
|
|
{
|
|
uint8_t header[4];
|
|
[data getBytes:header length:4];
|
|
if (header[0] == 'G' && header[1] == 'I' && header[2] == 'F' && header[3] == '8')
|
|
isGif = true;
|
|
}
|
|
if (isGif)
|
|
{
|
|
uploadMediaSignal = [TGUploadMediaSignals uploadFileWithContext:strongSelf->_shareContext data:data name:next[@"fileName"] == nil ? @"animation.gif" : next[@"fileName"] mimeType:@"image/gif" attributes:@[
|
|
[Api38_DocumentAttribute documentAttributeAnimated],
|
|
[Api38_DocumentAttribute documentAttributeImageSizeWithW:@((int32_t)image.size.width) h:@((int32_t)image.size.height)]
|
|
]];
|
|
}
|
|
else
|
|
{
|
|
image = TGScaleImage(image, TGFitSize(CGSizeMake(image.size.width * image.scale, image.size.height * image.scale), CGSizeMake(1280.0f, 1280.0f)));
|
|
NSData *imageData = UIImageJPEGRepresentation(image, 0.54f);
|
|
uploadMediaSignal = [TGUploadMediaSignals uploadPhotoWithContext:strongSelf->_shareContext data:imageData];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uploadMediaSignal = [TGUploadMediaSignals uploadFileWithContext:strongSelf->_shareContext data:data name:next[@"fileName"] mimeType:next[@"mimeType"] attributes:@[]];
|
|
}
|
|
}
|
|
else if ([next respondsToSelector:@selector(characterAtIndex:)])
|
|
return [SSignal single:[[TGUploadedMessageContentText alloc] initWithText:next]];
|
|
else
|
|
return [SSignal fail:nil];
|
|
|
|
if (uploadMediaSignal == nil)
|
|
return [SSignal fail:nil];
|
|
|
|
return [uploadMediaSignal mapToSignal:^SSignal *(id next)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
if ([next isKindOfClass:[Api38_InputMedia class]])
|
|
{
|
|
return [SSignal single:[[TGUploadedMessageContentMedia alloc] initWithInputMedia:next]];
|
|
}
|
|
else
|
|
return [SSignal single:@((providerIndex + [next floatValue]) / providers.count)];
|
|
}
|
|
else
|
|
return [SSignal fail:nil];
|
|
}];
|
|
}
|
|
else
|
|
return [SSignal fail:nil];
|
|
}];
|
|
|
|
if (sendMessageSignal != nil)
|
|
itemsSignal = [itemsSignal then:sendMessageSignal];
|
|
}
|
|
}
|
|
|
|
itemsSignal = [itemsSignal reduceLeftWithPassthrough:@[] with:^id (NSArray *currentUploadedMessageContents, id next, void (^passthrough)(id))
|
|
{
|
|
if ([next isKindOfClass:[TGUploadedMessageContent class]])
|
|
return [currentUploadedMessageContents arrayByAddingObject:next];
|
|
else
|
|
passthrough(next);
|
|
|
|
return currentUploadedMessageContents;
|
|
}];
|
|
|
|
itemsSignal = [itemsSignal mapToSignal:^SSignal *(id next)
|
|
{
|
|
if ([next respondsToSelector:@selector(floatValue)])
|
|
return [SSignal single:next];
|
|
else
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
SSignal *sendMessages = [SSignal complete];
|
|
for (id content in next)
|
|
{
|
|
if ([content isKindOfClass:[TGUploadedMessageContentText class]])
|
|
{
|
|
sendMessages = [sendMessages then:[TGSendMessageSignals sendTextMessageWithContext:strongSelf->_shareContext peerId:peerId users:users text:((TGUploadedMessageContentText *)content).text]];
|
|
}
|
|
else if ([content isKindOfClass:[TGUploadedMessageContentMedia class]])
|
|
{
|
|
sendMessages = [sendMessages then:[TGSendMessageSignals sendMediaWithContext:strongSelf->_shareContext peerId:peerId users:users inputMedia:((TGUploadedMessageContentMedia *)content).inputMedia]];
|
|
}
|
|
}
|
|
|
|
return sendMessages;
|
|
}
|
|
else
|
|
return [SSignal fail:nil];
|
|
}
|
|
}];
|
|
|
|
if (_sendMessagesDisposable == nil)
|
|
_sendMessagesDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
_progressAlert = [[TGProgressAlert alloc] initWithFrame:self.view.bounds];
|
|
_progressAlert.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_progressAlert.text = NSLocalizedString(@"Share.Sharing", nil);
|
|
_progressAlert.alpha = 0.0f;
|
|
[self.view addSubview:_progressAlert];
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
_progressAlert.alpha = 1.0f;
|
|
}];
|
|
|
|
_progressAlert.cancel = ^
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf->_sendMessagesDisposable setDisposable:nil];
|
|
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
strongSelf->_progressAlert.alpha = 0.0f;
|
|
} completion:^(BOOL finished)
|
|
{
|
|
[strongSelf->_progressAlert removeFromSuperview];
|
|
strongSelf->_progressAlert = nil;
|
|
}];
|
|
}
|
|
};
|
|
|
|
itemsSignal = [[itemsSignal then:[SSignal single:@(1.0f)]] then:[[SSignal complete] delay:0.4 onQueue:[SQueue mainQueue]]];
|
|
|
|
[_sendMessagesDisposable setDisposable:[[[itemsSignal deliverOn:[SQueue mainQueue]] onDispose:^
|
|
{
|
|
}] startWithNext:^(id next)
|
|
{
|
|
if ([next respondsToSelector:@selector(floatValue)])
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf->_progressAlert setProgress:[next floatValue] animated:true];
|
|
}
|
|
} error:^(id error)
|
|
{
|
|
NSLog(@"error: %@", error);
|
|
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
strongSelf->_progressAlert.alpha = 0.0f;
|
|
} completion:^(BOOL finished)
|
|
{
|
|
[strongSelf->_progressAlert removeFromSuperview];
|
|
strongSelf->_progressAlert = nil;
|
|
}];
|
|
}
|
|
} completed:^
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf animateDismiss];
|
|
|
|
self.view.userInteractionEnabled = false;
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
|
|
{
|
|
[strongSelf didSelectPost];
|
|
});
|
|
}
|
|
}]];
|
|
}
|
|
|
|
- (SSignal *)signalForDataItemProvider:(NSItemProvider *)itemProvider
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeData options:nil completionHandler:^(NSData *imageData, NSError *error)
|
|
{
|
|
if (error != nil)
|
|
[subscriber putError:nil];
|
|
else
|
|
{
|
|
[subscriber putNext:@{@"data": imageData}];
|
|
[subscriber putCompletion];
|
|
}
|
|
}];
|
|
|
|
return nil;
|
|
}];
|
|
}
|
|
|
|
- (SSignal *)signalForImageItemProvider:(NSItemProvider *)itemProvider
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeData options:nil completionHandler:^(UIImage *image, NSError *error)
|
|
{
|
|
if (error != nil)
|
|
[subscriber putError:nil];
|
|
else
|
|
{
|
|
[subscriber putNext:@{@"image": image}];
|
|
[subscriber putCompletion];
|
|
}
|
|
}];
|
|
|
|
return nil;
|
|
}];
|
|
}
|
|
|
|
- (SSignal *)signalForUrlItemProvider:(NSItemProvider *)itemProvider
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
[itemProvider loadItemForTypeIdentifier:@"public.file-url" options:nil completionHandler:^(NSURL *url, NSError *error)
|
|
{
|
|
if (error != nil)
|
|
[subscriber putError:nil];
|
|
else
|
|
{
|
|
[subscriber putNext:url];
|
|
[subscriber putCompletion];
|
|
}
|
|
}];
|
|
|
|
return nil;
|
|
}];
|
|
}
|
|
|
|
- (SSignal *)signalForTextItemProvider:(NSItemProvider *)itemProvider
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
[itemProvider loadItemForTypeIdentifier:@"public.text" options:nil completionHandler:^(NSString *text, NSError *error)
|
|
{
|
|
if (error != nil)
|
|
[subscriber putError:nil];
|
|
else
|
|
{
|
|
[subscriber putNext:text];
|
|
[subscriber putCompletion];
|
|
}
|
|
}];
|
|
|
|
return nil;
|
|
}];
|
|
}
|
|
|
|
- (SSignal *)signalForTextUrlItemProvider:(NSItemProvider *)itemProvider
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeURL options:nil completionHandler:^(NSURL *url, NSError *error)
|
|
{
|
|
if (error != nil)
|
|
[subscriber putError:nil];
|
|
else
|
|
{
|
|
[subscriber putNext:[url absoluteString]];
|
|
[subscriber putCompletion];
|
|
}
|
|
}];
|
|
|
|
return nil;
|
|
}];
|
|
}
|
|
|
|
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
|
|
{
|
|
if (editing)
|
|
{
|
|
[UIView animateWithDuration:0.25 animations:^
|
|
{
|
|
_navigationBar.frame = CGRectMake(0.0f, -_navigationBar.frame.size.height, _navigationBar.frame.size.width, _navigationBar.frame.size.height);
|
|
[_tableView setContentInset:UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f)];
|
|
[_tableView setScrollIndicatorInsets:UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f)];
|
|
[_tableView setScrollEnabled:false];
|
|
_searchDimView.frame = CGRectMake(0.0f, 20.0f + 44.0f, _searchDimView.frame.size.width, _searchDimView.frame.size.height);
|
|
_searchResultsTableView.frame = CGRectMake(0.0f, 20.0f + 44.0f, _searchDimView.frame.size.width, _searchDimView.frame.size.height - 20.0f - 44.0f - 44.0f);
|
|
_searchDimView.alpha = 1.0f;
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
[UIView animateWithDuration:0.25 animations:^
|
|
{
|
|
_navigationBar.frame = CGRectMake(0.0f, 0.0f, _navigationBar.frame.size.width, _navigationBar.frame.size.height);
|
|
[_tableView setScrollEnabled:true];
|
|
[_tableView setContentInset:UIEdgeInsetsMake(20.0f + 44.0f, 0.0f, 0.0f, 0.0f)];
|
|
[_tableView setScrollIndicatorInsets:UIEdgeInsetsMake(20.0f + 44.0f, 0.0f, 0.0f, 0.0f)];
|
|
[_tableView setContentOffset:CGPointMake(0.0f, -20.0f - 44.0f) animated:false];
|
|
_searchDimView.frame = CGRectMake(0.0f, 20.0f + 44.0f + 44.0f, _searchDimView.frame.size.width, _searchDimView.frame.size.height);
|
|
_searchResultsTableView.frame = CGRectMake(0.0f, 20.0f + 20.0f + 44.0f, _searchDimView.frame.size.width, _searchDimView.frame.size.height - 20.0f - 44.0f - 44.0f);
|
|
_searchResultsTableView.hidden = true;
|
|
_searchDimView.alpha = 0.0f;
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)searchDimViewTapped:(UITapGestureRecognizer *)recognizer
|
|
{
|
|
if (recognizer.state == UIGestureRecognizerStateEnded)
|
|
{
|
|
[self searchBarCancelButtonClicked:_searchBar];
|
|
}
|
|
}
|
|
|
|
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
|
|
{
|
|
[searchBar setShowsCancelButton:true animated:true];
|
|
[self setEditing:true animated:true];
|
|
}
|
|
|
|
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
|
{
|
|
[searchBar setText:@""];
|
|
[searchBar endEditing:true];
|
|
[searchBar setShowsCancelButton:false animated:true];
|
|
[self setEditing:false animated:true];
|
|
|
|
_searchSections = nil;
|
|
_searchResultsTableView.hidden = true;
|
|
[_searchResultsTableView reloadData];
|
|
[_searchDisposable setDisposable:nil];
|
|
}
|
|
|
|
- (void)searchBar:(UISearchBar *)__unused searchBar textDidChange:(NSString *)searchText
|
|
{
|
|
NSString *text = searchText;
|
|
if (text.length == 0)
|
|
{
|
|
_searchSections = nil;
|
|
_searchResultsTableView.hidden = true;
|
|
[_searchResultsTableView reloadData];
|
|
[_searchDisposable setDisposable:nil];
|
|
}
|
|
else
|
|
{
|
|
_searchResultsTableView.hidden = false;
|
|
if (_searchDisposable == nil)
|
|
_searchDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
__weak ShareViewController *weakSelf = self;
|
|
|
|
SSignal *searchChatsSignal = [TGSearchSignals searchChatsWithContext:_shareContext chats:_chatModels users:_userModels query:text];
|
|
SSignal *searchRemoteSignal = [[[SSignal complete] delay:0.1 onQueue:[SQueue concurrentDefaultQueue]] then:[TGSearchSignals searchUsersWithContext:_shareContext query:text]];
|
|
SSignal *searchSignal = [SSignal combineSignals:@[searchRemoteSignal, searchChatsSignal] withInitialStates:@[@{@"chats": @[], @"users": @[]}]];
|
|
|
|
[_searchDisposable setDisposable:[[searchSignal deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *results)
|
|
{
|
|
__strong ShareViewController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
NSMutableArray *sections = [[NSMutableArray alloc] init];
|
|
if (((NSArray *)results[1][@"chats"]).count != 0)
|
|
[sections addObject:@{@"chats": results[1][@"chats"], @"users": results[1][@"users"]}];
|
|
if (((NSArray *)results[0][@"chats"]).count != 0)
|
|
[sections addObject:@{@"chats": results[0][@"chats"], @"users": results[0][@"users"]}];
|
|
|
|
[strongSelf setSearchResultsSections:sections];
|
|
}
|
|
}]];
|
|
}
|
|
}
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
{
|
|
if (scrollView == _searchResultsTableView)
|
|
{
|
|
[self.view endEditing:true];
|
|
}
|
|
}
|
|
|
|
@end
|