PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Telegraph/TGMediaAssetsPickerController.m

https://gitlab.com/peternagel/telegram_chat
Objective C | 382 lines | 303 code | 79 blank | 0 comment | 65 complexity | b806997a10cddf142c00df1a4712dd3f MD5 | raw file
  1. #import "TGMediaAssetsPickerController.h"
  2. #import "UICollectionView+Utils.h"
  3. #import "TGMediaPickerLayoutMetrics.h"
  4. #import "TGMediaAssetsPhotoCell.h"
  5. #import "TGMediaAssetsVideoCell.h"
  6. #import "TGMediaAssetsGifCell.h"
  7. #import "TGMediaAssetsUtils.h"
  8. #import "TGMediaAssetImageSignals.h"
  9. #import "TGMediaAssetFetchResultChange.h"
  10. #import "TGModernBarButton.h"
  11. #import "TGMediaAsset+TGMediaEditableItem.h"
  12. #import "TGPhotoEditorController.h"
  13. #import "PGPhotoEditorValues.h"
  14. #import "TGMediaPickerModernGalleryMixin.h"
  15. #import "TGMediaPickerGalleryItem.h"
  16. @interface TGMediaAssetsPickerController ()
  17. {
  18. TGMediaAssetsControllerIntent _intent;
  19. TGMediaAssetsLibrary *_assetsLibrary;
  20. SMetaDisposable *_assetsDisposable;
  21. TGMediaAssetFetchResult *_fetchResult;
  22. TGMediaAssetsPreheatMixin *_preheatMixin;
  23. TGModernBarButton *_searchBarButton;
  24. TGMediaPickerModernGalleryMixin *_galleryMixin;
  25. }
  26. @end
  27. @implementation TGMediaAssetsPickerController
  28. - (instancetype)initWithAssetsLibrary:(TGMediaAssetsLibrary *)assetsLibrary assetGroup:(TGMediaAssetGroup *)assetGroup intent:(TGMediaAssetsControllerIntent)intent selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext
  29. {
  30. bool hasSelection = false;
  31. bool hasEditing = false;
  32. switch (intent)
  33. {
  34. case TGMediaAssetsControllerSendMediaIntent:
  35. hasSelection = true;
  36. hasEditing = true;
  37. break;
  38. case TGMediaAssetsControllerSendFileIntent:
  39. hasSelection = true;
  40. hasEditing = true;
  41. break;
  42. case TGMediaAssetsControllerSetProfilePhotoIntent:
  43. hasEditing = true;
  44. break;
  45. default:
  46. break;
  47. }
  48. self = [super initWithSelectionContext:hasSelection ? selectionContext : nil editingContext:hasEditing ? editingContext : nil];
  49. if (self != nil)
  50. {
  51. _assetsLibrary = assetsLibrary;
  52. _assetGroup = assetGroup;
  53. _intent = intent;
  54. [self setTitle:_assetGroup.title];
  55. _assetsDisposable = [[SMetaDisposable alloc] init];
  56. }
  57. return self;
  58. }
  59. - (void)dealloc
  60. {
  61. [_assetsDisposable dispose];
  62. }
  63. - (void)loadView
  64. {
  65. [super loadView];
  66. [_collectionView registerClass:[TGMediaAssetsPhotoCell class] forCellWithReuseIdentifier:TGMediaAssetsPhotoCellKind];
  67. [_collectionView registerClass:[TGMediaAssetsVideoCell class] forCellWithReuseIdentifier:TGMediaAssetsVideoCellKind];
  68. [_collectionView registerClass:[TGMediaAssetsGifCell class] forCellWithReuseIdentifier:TGMediaAssetsGifCellKind];
  69. __weak TGMediaAssetsPickerController *weakSelf = self;
  70. _preheatMixin = [[TGMediaAssetsPreheatMixin alloc] initWithCollectionView:_collectionView scrollDirection:UICollectionViewScrollDirectionVertical];
  71. _preheatMixin.imageType = TGMediaAssetImageTypeThumbnail;
  72. _preheatMixin.assetCount = ^NSInteger
  73. {
  74. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  75. if (strongSelf == nil)
  76. return 0;
  77. return strongSelf->_fetchResult.count;
  78. };
  79. _preheatMixin.assetAtIndex = ^TGMediaAsset *(NSInteger index)
  80. {
  81. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  82. if (strongSelf == nil)
  83. return nil;
  84. return [strongSelf->_fetchResult assetAtIndex:index];
  85. };
  86. }
  87. - (void)viewDidLoad
  88. {
  89. [super viewDidLoad];
  90. [self setRightBarButtonItem:[(TGMediaAssetsController *)self.navigationController rightBarButtonItem]];
  91. SSignal *groupSignal = nil;
  92. if (_assetGroup != nil)
  93. groupSignal = [SSignal single:_assetGroup];
  94. else
  95. groupSignal = [_assetsLibrary cameraRollGroup];
  96. __weak TGMediaAssetsPickerController *weakSelf = self;
  97. [_assetsDisposable setDisposable:[[[[groupSignal deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(TGMediaAssetGroup *assetGroup)
  98. {
  99. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  100. if (strongSelf == nil)
  101. return nil;
  102. if (strongSelf->_assetGroup == nil)
  103. strongSelf->_assetGroup = assetGroup;
  104. [strongSelf setTitle:assetGroup.title];
  105. return [strongSelf->_assetsLibrary assetsOfAssetGroup:assetGroup reversed:false];
  106. }] deliverOn:[SQueue mainQueue]] startWithNext:^(id next)
  107. {
  108. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  109. if (strongSelf == nil)
  110. return;
  111. if (strongSelf->_layoutMetrics == nil)
  112. {
  113. if (strongSelf->_assetGroup.subtype == TGMediaAssetGroupSubtypePanoramas)
  114. strongSelf->_layoutMetrics = [TGMediaPickerLayoutMetrics panoramaLayoutMetrics];
  115. else
  116. strongSelf->_layoutMetrics = [TGMediaPickerLayoutMetrics defaultLayoutMetrics];
  117. strongSelf->_preheatMixin.imageSize = [strongSelf->_layoutMetrics imageSize];
  118. }
  119. if ([next isKindOfClass:[TGMediaAssetFetchResult class]])
  120. {
  121. TGMediaAssetFetchResult *fetchResult = (TGMediaAssetFetchResult *)next;
  122. bool scrollToBottom = (strongSelf->_fetchResult == nil);
  123. strongSelf->_fetchResult = fetchResult;
  124. [strongSelf->_collectionView reloadData];
  125. if (scrollToBottom)
  126. {
  127. [strongSelf->_collectionView layoutSubviews];
  128. [strongSelf _adjustContentOffsetToBottom];
  129. }
  130. }
  131. else if ([next isKindOfClass:[TGMediaAssetFetchResultChange class]])
  132. {
  133. TGMediaAssetFetchResultChange *change = (TGMediaAssetFetchResultChange *)next;
  134. strongSelf->_fetchResult = change.fetchResultAfterChanges;
  135. [TGMediaAssetsCollectionViewIncrementalUpdater updateCollectionView:strongSelf->_collectionView withChange:change completion:nil];
  136. }
  137. if (strongSelf->_galleryMixin != nil && strongSelf->_fetchResult != nil)
  138. [strongSelf->_galleryMixin updateWithFetchResult:strongSelf->_fetchResult];
  139. }]];
  140. }
  141. #pragma mark -
  142. - (NSInteger)_numberOfItems
  143. {
  144. return _fetchResult.count;
  145. }
  146. - (id)_itemAtIndexPath:(NSIndexPath *)indexPath
  147. {
  148. return [_fetchResult assetAtIndex:indexPath.row];
  149. }
  150. - (SSignal *)_signalForItem:(id)item
  151. {
  152. SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:item imageType:TGMediaAssetImageTypeThumbnail size:[_layoutMetrics imageSize]];
  153. return [[self.editingContext thumbnailImageSignalForItem:item] mapToSignal:^SSignal *(id result)
  154. {
  155. if (result != nil)
  156. return [SSignal single:result];
  157. else
  158. return assetSignal;
  159. }];
  160. }
  161. - (NSString *)_cellKindForItem:(id)item
  162. {
  163. TGMediaAsset *asset = (TGMediaAsset *)item;
  164. if ([asset isKindOfClass:[TGMediaAsset class]])
  165. {
  166. switch (asset.type)
  167. {
  168. case TGMediaAssetVideoType:
  169. return TGMediaAssetsVideoCellKind;
  170. case TGMediaAssetGifType:
  171. if (_intent == TGMediaAssetsControllerSetProfilePhotoIntent)
  172. return TGMediaAssetsPhotoCellKind;
  173. else
  174. return TGMediaAssetsGifCellKind;
  175. default:
  176. break;
  177. }
  178. }
  179. return TGMediaAssetsPhotoCellKind;
  180. }
  181. #pragma mark - Collection View Delegate
  182. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  183. {
  184. NSInteger index = indexPath.row;
  185. TGMediaAsset *asset = [_fetchResult assetAtIndex:index];
  186. __block UIImage *thumbnailImage = nil;
  187. if ([TGMediaAssetsLibrary usesPhotoFramework])
  188. {
  189. TGMediaPickerCell *cell = (TGMediaPickerCell *)[collectionView cellForItemAtIndexPath:indexPath];
  190. if ([cell isKindOfClass:[TGMediaPickerCell class]])
  191. thumbnailImage = cell.imageView.image;
  192. }
  193. else
  194. {
  195. [[TGMediaAssetImageSignals imageForAsset:asset imageType:TGMediaAssetImageTypeAspectRatioThumbnail size:CGSizeZero] startWithNext:^(UIImage *next)
  196. {
  197. thumbnailImage = next;
  198. }];
  199. }
  200. __weak TGMediaAssetsPickerController *weakSelf = self;
  201. if (_intent == TGMediaAssetsControllerSetProfilePhotoIntent)
  202. {
  203. TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithItem:asset intent:TGPhotoEditorControllerAvatarIntent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
  204. controller.didFinishRenderingFullSizeImage = ^(UIImage *resultImage)
  205. {
  206. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  207. if (strongSelf == nil)
  208. return;
  209. [[strongSelf->_assetsLibrary saveAssetWithImage:resultImage] startWithNext:nil];
  210. };
  211. controller.didFinishEditing = ^(__unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, bool hasChanges)
  212. {
  213. if (!hasChanges)
  214. return;
  215. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  216. if (strongSelf == nil)
  217. return;
  218. [(TGMediaAssetsController *)strongSelf.navigationController completeWithAvatarImage:resultImage];
  219. };
  220. controller.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
  221. {
  222. return [editableItem thumbnailImageSignal];
  223. };
  224. controller.requestOriginalScreenSizeImage = ^(id<TGMediaEditableItem> editableItem)
  225. {
  226. return [editableItem screenImageSignal];
  227. };
  228. controller.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> editableItem)
  229. {
  230. return [editableItem originalImageSignal];
  231. };
  232. [self.navigationController pushViewController:controller animated:true];
  233. }
  234. else
  235. {
  236. bool hasCaption = (_intent == TGMediaAssetsControllerSendMediaIntent && self.captionsEnabled);
  237. bool asFile = (_intent == TGMediaAssetsControllerSendFileIntent);
  238. _galleryMixin = [[TGMediaPickerModernGalleryMixin alloc] initWithItem:asset fetchResult:_fetchResult parentController:self thumbnailImage:thumbnailImage selectionContext:self.selectionContext editingContext:self.editingContext suggestionContext:self.suggestionContext hasCaption:hasCaption asFile:asFile itemsLimit:0];
  239. _galleryMixin.thumbnailSignalForItem = ^SSignal *(id item)
  240. {
  241. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  242. if (strongSelf == nil)
  243. return nil;
  244. return [strongSelf _signalForItem:item];
  245. };
  246. _galleryMixin.referenceViewForItem = ^UIView *(TGMediaPickerGalleryItem *item)
  247. {
  248. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  249. if (strongSelf == nil)
  250. return nil;
  251. for (TGMediaPickerCell *cell in [strongSelf->_collectionView visibleCells])
  252. {
  253. if ([cell.item isEqual:item.asset])
  254. return cell;
  255. }
  256. return nil;
  257. };
  258. _galleryMixin.itemFocused = ^(TGMediaPickerGalleryItem *item)
  259. {
  260. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  261. if (strongSelf == nil)
  262. return;
  263. [strongSelf _hideCellForItem:item.asset animated:false];
  264. };
  265. _galleryMixin.didTransitionOut = ^
  266. {
  267. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  268. if (strongSelf == nil)
  269. return;
  270. [strongSelf _hideCellForItem:nil animated:true];
  271. strongSelf->_galleryMixin = nil;
  272. };
  273. _galleryMixin.completeWithItem = ^(TGMediaPickerGalleryItem *item)
  274. {
  275. __strong TGMediaAssetsPickerController *strongSelf = weakSelf;
  276. if (strongSelf == nil)
  277. return;
  278. [(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset];
  279. };
  280. [_galleryMixin present];
  281. }
  282. }
  283. #pragma mark - Asset Image Preheating
  284. - (void)scrollViewDidScroll:(UIScrollView *)__unused scrollView
  285. {
  286. bool isViewVisible = (self.isViewLoaded && self.view.window != nil);
  287. if (!isViewVisible)
  288. return;
  289. [_preheatMixin update];
  290. }
  291. - (NSArray *)_assetsAtIndexPaths:(NSArray *)indexPaths
  292. {
  293. if (indexPaths.count == 0)
  294. return nil;
  295. NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
  296. for (NSIndexPath *indexPath in indexPaths)
  297. {
  298. if ((NSUInteger)indexPath.row < _fetchResult.count)
  299. [assets addObject:[_fetchResult assetAtIndex:indexPath.row]];
  300. }
  301. return assets;
  302. }
  303. @end