mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1291 lines
49 KiB
Objective-C
1291 lines
49 KiB
Objective-C
#import "TGAttachmentCarouselItemView.h"
|
|
|
|
#import "LegacyComponentsInternal.h"
|
|
|
|
#import <LegacyComponents/TGMenuSheetButtonItemView.h>
|
|
#import <LegacyComponents/TGMenuSheetView.h>
|
|
|
|
#import <LegacyComponents/UICollectionView+Utils.h>
|
|
#import <LegacyComponents/TGPhotoEditorUtils.h>
|
|
|
|
#import <LegacyComponents/TGMediaEditingContext.h>
|
|
#import <LegacyComponents/TGMediaSelectionContext.h>
|
|
|
|
#import "TGTransitionLayout.h"
|
|
|
|
#import "TGAttachmentCameraView.h"
|
|
|
|
#import "TGAttachmentPhotoCell.h"
|
|
#import "TGAttachmentVideoCell.h"
|
|
#import "TGAttachmentGifCell.h"
|
|
|
|
#import <LegacyComponents/TGMediaAssetsLibrary.h>
|
|
#import <LegacyComponents/TGMediaAssetFetchResult.h>
|
|
|
|
#import <LegacyComponents/TGMediaAssetImageSignals.h>
|
|
|
|
#import <LegacyComponents/TGMediaPickerModernGalleryMixin.h>
|
|
#import <LegacyComponents/TGMediaPickerGalleryItem.h>
|
|
#import <LegacyComponents/TGMediaAssetsUtils.h>
|
|
|
|
#import "TGMediaAvatarEditorTransition.h"
|
|
#import <LegacyComponents/TGPhotoEditorController.h>
|
|
#import <LegacyComponents/TGVideoEditAdjustments.h>
|
|
#import <LegacyComponents/TGMediaAsset+TGMediaEditableItem.h>
|
|
|
|
const CGSize TGAttachmentCellSize = { 84.0f, 84.0f };
|
|
const CGFloat TGAttachmentEdgeInset = 8.0f;
|
|
|
|
const CGFloat TGAttachmentZoomedPhotoRemainer = 32.0f;
|
|
|
|
const CGFloat TGAttachmentZoomedPhotoHeight = 198.0f;
|
|
const CGFloat TGAttachmentZoomedPhotoMaxWidth = 250.0f;
|
|
|
|
const CGFloat TGAttachmentZoomedPhotoCondensedHeight = 141.0f;
|
|
const CGFloat TGAttachmentZoomedPhotoCondensedMaxWidth = 178.0f;
|
|
|
|
const CGFloat TGAttachmentZoomedPhotoAspectRatio = 1.2626f;
|
|
|
|
const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
|
|
|
@interface TGAttachmentCarouselCollectionView () <TGModernGalleryTransitionHostScrollView>
|
|
|
|
@end
|
|
|
|
@implementation TGAttachmentCarouselCollectionView
|
|
|
|
- (bool)disableGalleryTransitionOffsetFix {
|
|
return true;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface TGAttachmentCarouselItemView () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
|
|
{
|
|
TGMediaAssetsLibrary *_assetsLibrary;
|
|
SMetaDisposable *_assetsDisposable;
|
|
TGMediaAssetFetchResult *_fetchResult;
|
|
|
|
bool _document;
|
|
bool _forProfilePhoto;
|
|
|
|
SMetaDisposable *_selectionChangedDisposable;
|
|
SMetaDisposable *_itemsSizeChangedDisposable;
|
|
|
|
UICollectionViewFlowLayout *_smallLayout;
|
|
UICollectionViewFlowLayout *_largeLayout;
|
|
UICollectionView *_collectionView;
|
|
TGMediaAssetsPreheatMixin *_preheatMixin;
|
|
|
|
TGAttachmentCameraView *_cameraView;
|
|
|
|
TGMenuSheetButtonItemView *_sendMediaItemView;
|
|
TGMenuSheetButtonItemView *_sendFileItemView;
|
|
|
|
TGMediaPickerModernGalleryMixin *_galleryMixin;
|
|
TGMediaPickerModernGalleryMixin *_previewGalleryMixin;
|
|
TGMediaAsset *_hiddenItem;
|
|
|
|
bool _zoomedIn;
|
|
bool _zoomingIn;
|
|
CGFloat _zoomingProgress;
|
|
|
|
NSInteger _pivotInItemIndex;
|
|
NSInteger _pivotOutItemIndex;
|
|
|
|
CGSize _imageSize;
|
|
|
|
CGSize _maxPhotoSize;
|
|
|
|
CGFloat _smallActivationHeight;
|
|
bool _smallActivated;
|
|
CGSize _smallMaxPhotoSize;
|
|
|
|
CGFloat _carouselCorrection;
|
|
|
|
id<LegacyComponentsContext> _context;
|
|
bool _saveEditedPhotos;
|
|
|
|
TGMenuSheetPallete *_pallete;
|
|
}
|
|
@end
|
|
|
|
@implementation TGAttachmentCarouselItemView
|
|
|
|
- (bool)disableGalleryTransitionOffsetFix {
|
|
return true;
|
|
}
|
|
|
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context camera:(bool)hasCamera selfPortrait:(bool)selfPortrait forProfilePhoto:(bool)forProfilePhoto assetType:(TGMediaAssetType)assetType saveEditedPhotos:(bool)saveEditedPhotos allowGrouping:(bool)allowGrouping
|
|
{
|
|
return [self initWithContext:context camera:hasCamera selfPortrait:selfPortrait forProfilePhoto:forProfilePhoto assetType:assetType saveEditedPhotos:saveEditedPhotos allowGrouping:allowGrouping allowSelection:true allowEditing:!selfPortrait document:false selectionLimit:30];
|
|
}
|
|
|
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context camera:(bool)hasCamera selfPortrait:(bool)selfPortrait forProfilePhoto:(bool)forProfilePhoto assetType:(TGMediaAssetType)assetType saveEditedPhotos:(bool)saveEditedPhotos allowGrouping:(bool)allowGrouping allowSelection:(bool)allowSelection allowEditing:(bool)allowEditing document:(bool)document selectionLimit:(int)selectionLimit
|
|
{
|
|
self = [super initWithType:TGMenuSheetItemTypeDefault];
|
|
if (self != nil)
|
|
{
|
|
_context = context;
|
|
_saveEditedPhotos = saveEditedPhotos;
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
hasCamera = true;
|
|
#endif
|
|
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
_document = document;
|
|
_forProfilePhoto = forProfilePhoto;
|
|
|
|
_assetsLibrary = [TGMediaAssetsLibrary libraryForAssetType:assetType];
|
|
_assetsDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
if (!forProfilePhoto && allowSelection)
|
|
{
|
|
_selectionContext = [[TGMediaSelectionContext alloc] initWithGroupingAllowed:allowGrouping selectionLimit:selectionLimit];
|
|
_selectionContext.selectionLimitExceeded = ^{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil) {
|
|
return;
|
|
}
|
|
if (strongSelf->_selectionLimitExceeded) {
|
|
strongSelf->_selectionLimitExceeded();
|
|
}
|
|
};
|
|
if (allowGrouping)
|
|
_selectionContext.grouping = true;
|
|
[_selectionContext setItemSourceUpdatedSignal:[_assetsLibrary libraryChanged]];
|
|
_selectionContext.updatedItemsSignal = ^SSignal *(NSArray *items)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return nil;
|
|
|
|
return [strongSelf->_assetsLibrary updatedAssetsForAssets:items];
|
|
};
|
|
|
|
_selectionChangedDisposable = [[SMetaDisposable alloc] init];
|
|
[_selectionChangedDisposable setDisposable:[[[_selectionContext selectionChangedSignal] mapToSignal:^SSignal *(id value)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return [SSignal complete];
|
|
|
|
return [[strongSelf->_collectionView noOngoingTransitionSignal] then:[SSignal single:value]];
|
|
}] startWithNext:^(__unused TGMediaSelectionChange *change)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
NSInteger index = 0;
|
|
if ([change.item isKindOfClass:[TGCameraCapturedVideo class]]) {
|
|
index = [strongSelf->_fetchResult indexOfAsset:((TGCameraCapturedVideo *)change.item).originalAsset];
|
|
} else {
|
|
index = [strongSelf->_fetchResult indexOfAsset:(TGMediaAsset *)change.item];
|
|
}
|
|
|
|
[strongSelf updateSendButtonsFromIndex:index];
|
|
|
|
[strongSelf updateSelectionIndexes];
|
|
}]];
|
|
}
|
|
|
|
if (allowEditing)
|
|
{
|
|
_editingContext = [[TGMediaEditingContext alloc] init];
|
|
|
|
_itemsSizeChangedDisposable = [[SMetaDisposable alloc] init];
|
|
[_itemsSizeChangedDisposable setDisposable:[[[_editingContext cropAdjustmentsUpdatedSignal] deliverOn:[SQueue mainQueue]] startWithNext:^(__unused id next)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
if (strongSelf->_zoomedIn)
|
|
{
|
|
[strongSelf->_largeLayout invalidateLayout];
|
|
[strongSelf->_collectionView layoutSubviews];
|
|
|
|
UICollectionViewCell *pivotCell = (UICollectionViewCell *)[strongSelf->_galleryMixin currentReferenceView];
|
|
if (pivotCell != nil)
|
|
{
|
|
NSIndexPath *indexPath = [strongSelf->_collectionView indexPathForCell:pivotCell];
|
|
if (indexPath != nil)
|
|
[strongSelf centerOnItemWithIndex:indexPath.row animated:false];
|
|
}
|
|
}
|
|
}]];
|
|
}
|
|
|
|
_smallLayout = [[UICollectionViewFlowLayout alloc] init];
|
|
_smallLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
|
_smallLayout.minimumLineSpacing = TGAttachmentEdgeInset;
|
|
|
|
_largeLayout = [[UICollectionViewFlowLayout alloc] init];
|
|
_largeLayout.scrollDirection = _smallLayout.scrollDirection;
|
|
_largeLayout.minimumLineSpacing = _smallLayout.minimumLineSpacing;
|
|
|
|
if (hasCamera)
|
|
{
|
|
_cameraView = [[TGAttachmentCameraView alloc] initForSelfPortrait:selfPortrait];
|
|
_cameraView.frame = CGRectMake(_smallLayout.minimumLineSpacing, 0, TGAttachmentCellSize.width, TGAttachmentCellSize.height);
|
|
[_cameraView startPreview];
|
|
|
|
_cameraView.pressed = ^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf.superview bringSubviewToFront:strongSelf];
|
|
|
|
if (strongSelf.cameraPressed != nil)
|
|
strongSelf.cameraPressed(strongSelf->_cameraView);
|
|
};
|
|
}
|
|
|
|
_collectionView = [[TGAttachmentCarouselCollectionView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, TGAttachmentZoomedPhotoHeight + TGAttachmentEdgeInset * 2) collectionViewLayout:_smallLayout];
|
|
if (iosMajorVersion() >= 11)
|
|
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
|
_collectionView.backgroundColor = [UIColor clearColor];
|
|
_collectionView.dataSource = self;
|
|
_collectionView.delegate = self;
|
|
_collectionView.showsHorizontalScrollIndicator = false;
|
|
_collectionView.showsVerticalScrollIndicator = false;
|
|
[_collectionView registerClass:[TGAttachmentPhotoCell class] forCellWithReuseIdentifier:TGAttachmentPhotoCellIdentifier];
|
|
[_collectionView registerClass:[TGAttachmentVideoCell class] forCellWithReuseIdentifier:TGAttachmentVideoCellIdentifier];
|
|
[_collectionView registerClass:[TGAttachmentGifCell class] forCellWithReuseIdentifier:TGAttachmentGifCellIdentifier];
|
|
if (iosMajorVersion() >= 9)
|
|
_collectionView.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
|
[self addSubview:_collectionView];
|
|
|
|
if (_cameraView)
|
|
[_collectionView addSubview:_cameraView];
|
|
|
|
_sendMediaItemView = [[TGMenuSheetButtonItemView alloc] initWithTitle:nil type:TGMenuSheetButtonTypeSend fontSize:20.0 action:^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
|
{
|
|
if (strongSelf->_selectionContext.allowGrouping)
|
|
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
|
strongSelf.sendPressed(nil, false, false, 0);
|
|
}
|
|
}];
|
|
[_sendMediaItemView setHidden:true animated:false];
|
|
[self addSubview:_sendMediaItemView];
|
|
|
|
if (!_document)
|
|
{
|
|
_sendFileItemView = [[TGMenuSheetButtonItemView alloc] initWithTitle:nil type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
|
strongSelf.sendPressed(nil, true, false, 0);
|
|
}];
|
|
_sendFileItemView.requiresDivider = false;
|
|
[_sendFileItemView setHidden:true animated:false];
|
|
[self addSubview:_sendFileItemView];
|
|
}
|
|
|
|
[self setSignal:[[TGMediaAssetsLibrary authorizationStatusSignal] mapToSignal:^SSignal *(NSNumber *statusValue)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return [SSignal complete];
|
|
|
|
TGMediaLibraryAuthorizationStatus status = [statusValue intValue];
|
|
if (status == TGMediaLibraryAuthorizationStatusAuthorized)
|
|
{
|
|
return [[strongSelf->_assetsLibrary cameraRollGroup] mapToSignal:^SSignal *(TGMediaAssetGroup *cameraRollGroup)
|
|
{
|
|
return [strongSelf->_assetsLibrary assetsOfAssetGroup:cameraRollGroup reversed:true];
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
return [SSignal fail:nil];
|
|
}
|
|
}]];
|
|
|
|
_preheatMixin = [[TGMediaAssetsPreheatMixin alloc] initWithCollectionView:_collectionView scrollDirection:UICollectionViewScrollDirectionHorizontal];
|
|
_preheatMixin.imageType = TGMediaAssetImageTypeThumbnail;
|
|
_preheatMixin.assetCount = ^NSInteger
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return 0;
|
|
|
|
return [strongSelf collectionView:strongSelf->_collectionView numberOfItemsInSection:0];
|
|
};
|
|
_preheatMixin.assetAtIndexPath = ^TGMediaAsset *(NSIndexPath *indexPath)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return nil;
|
|
|
|
return [strongSelf->_fetchResult assetAtIndex:indexPath.row];
|
|
};
|
|
|
|
[self _updateImageSize];
|
|
_preheatMixin.imageSize = _imageSize;
|
|
|
|
[self setCondensed:false];
|
|
|
|
_pivotInItemIndex = NSNotFound;
|
|
_pivotOutItemIndex = NSNotFound;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_assetsDisposable dispose];
|
|
[_selectionChangedDisposable dispose];
|
|
[_itemsSizeChangedDisposable dispose];
|
|
}
|
|
|
|
- (void)setPallete:(TGMenuSheetPallete *)pallete
|
|
{
|
|
_pallete = pallete;
|
|
|
|
_cameraView.pallete = pallete;
|
|
[_sendMediaItemView setPallete:pallete];
|
|
[_sendFileItemView setPallete:pallete];
|
|
}
|
|
|
|
- (void)setRemainingHeight:(CGFloat)remainingHeight
|
|
{
|
|
_remainingHeight = remainingHeight;
|
|
[self setCondensed:_condensed];
|
|
}
|
|
|
|
- (void)updateSelectionIndexes
|
|
{
|
|
for (TGAttachmentAssetCell *cell in _collectionView.visibleCells)
|
|
{
|
|
NSUInteger index = [self.selectionContext indexOfItem:cell.asset];
|
|
[cell.checkButton setNumber:index];
|
|
}
|
|
}
|
|
|
|
- (void)setCondensed:(bool)condensed
|
|
{
|
|
_condensed = condensed;
|
|
|
|
if (condensed)
|
|
_maxPhotoSize = CGSizeMake(TGAttachmentZoomedPhotoCondensedMaxWidth, TGAttachmentZoomedPhotoCondensedHeight);
|
|
else
|
|
_maxPhotoSize = CGSizeMake(TGAttachmentZoomedPhotoMaxWidth, TGAttachmentZoomedPhotoHeight);
|
|
|
|
if (_remainingHeight > TGMenuSheetButtonItemViewHeight * (condensed ? 3 : 4))
|
|
_maxPhotoSize.height += TGAttachmentZoomedPhotoRemainer;
|
|
|
|
CGSize screenSize = TGScreenSize();
|
|
_smallActivationHeight = screenSize.width;
|
|
|
|
CGFloat smallHeight = MAX(95, screenSize.width - 225);
|
|
_smallMaxPhotoSize = CGSizeMake(ceil(smallHeight * TGAttachmentZoomedPhotoAspectRatio), smallHeight);
|
|
|
|
CGRect frame = _collectionView.frame;
|
|
frame.size.height = _maxPhotoSize.height + TGAttachmentEdgeInset * 2;
|
|
_collectionView.frame = frame;
|
|
}
|
|
|
|
- (void)setSignal:(SSignal *)signal
|
|
{
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
[_assetsDisposable setDisposable:[[[signal mapToSignal:^SSignal *(id value)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return [SSignal complete];
|
|
|
|
return [[strongSelf->_collectionView noOngoingTransitionSignal] then:[SSignal single:value]];
|
|
}] deliverOn:[SQueue mainQueue]] startWithNext:^(id next)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
if ([next isKindOfClass:[TGMediaAssetFetchResult class]])
|
|
{
|
|
TGMediaAssetFetchResult *fetchResult = (TGMediaAssetFetchResult *)next;
|
|
strongSelf->_fetchResult = fetchResult;
|
|
[strongSelf->_collectionView reloadData];
|
|
}
|
|
else if ([next isKindOfClass:[TGMediaAssetFetchResultChange class]])
|
|
{
|
|
TGMediaAssetFetchResultChange *change = (TGMediaAssetFetchResultChange *)next;
|
|
strongSelf->_fetchResult = change.fetchResultAfterChanges;
|
|
[TGMediaAssetsCollectionViewIncrementalUpdater updateCollectionView:strongSelf->_collectionView withChange:change completion:nil];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
[strongSelf scrollViewDidScroll:strongSelf->_collectionView];
|
|
});
|
|
}
|
|
|
|
if (strongSelf->_galleryMixin != nil && strongSelf->_fetchResult != nil)
|
|
[strongSelf->_galleryMixin updateWithFetchResult:strongSelf->_fetchResult];
|
|
}]];
|
|
}
|
|
|
|
- (SSignal *)_signalForItem:(TGMediaAsset *)asset
|
|
{
|
|
return [self _signalForItem:asset refresh:false onlyThumbnail:false];
|
|
}
|
|
|
|
- (SSignal *)_signalForItem:(TGMediaAsset *)asset refresh:(bool)refresh onlyThumbnail:(bool)onlyThumbnail
|
|
{
|
|
bool thumbnail = onlyThumbnail || !_zoomedIn;
|
|
CGSize imageSize = onlyThumbnail ? [self imageSizeForThumbnail:true] : _imageSize;
|
|
|
|
TGMediaAssetImageType screenImageType = refresh ? TGMediaAssetImageTypeLargeThumbnail : TGMediaAssetImageTypeFastLargeThumbnail;
|
|
TGMediaAssetImageType imageType = thumbnail ? TGMediaAssetImageTypeAspectRatioThumbnail : screenImageType;
|
|
|
|
TGMediaAsset *concreteAsset = asset;
|
|
if ([concreteAsset isKindOfClass:[TGCameraCapturedVideo class]]) {
|
|
concreteAsset = [(TGCameraCapturedVideo *)asset originalAsset];
|
|
}
|
|
|
|
SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:concreteAsset imageType:imageType size:imageSize];
|
|
if (_editingContext == nil)
|
|
return assetSignal;
|
|
|
|
SSignal *editedSignal = thumbnail ? [_editingContext thumbnailImageSignalForItem:asset] : [_editingContext fastImageSignalForItem:asset withUpdates:true];
|
|
return [editedSignal mapToSignal:^SSignal *(id result)
|
|
{
|
|
if (result != nil)
|
|
return [SSignal single:result];
|
|
else
|
|
return assetSignal;
|
|
}];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)setCameraZoomedIn:(bool)zoomedIn progress:(CGFloat)progress
|
|
{
|
|
if (_cameraView == nil)
|
|
return;
|
|
|
|
CGFloat size = TGAttachmentCellSize.height;
|
|
progress = zoomedIn ? progress : 1.0f - progress;
|
|
_cameraView.frame = CGRectMake(_smallLayout.minimumLineSpacing - (size + _smallLayout.minimumLineSpacing) * progress, 0, TGAttachmentCellSize.width + (size - TGAttachmentCellSize.width) * progress, TGAttachmentCellSize.height + (size - TGAttachmentCellSize.height) * progress);
|
|
[_cameraView setZoomedProgress:progress];
|
|
}
|
|
|
|
- (void)setZoomedMode:(bool)zoomed animated:(bool)animated index:(NSInteger)index
|
|
{
|
|
if (zoomed == _zoomedIn)
|
|
{
|
|
if (_zoomedIn)
|
|
[self centerOnItemWithIndex:index animated:animated];
|
|
|
|
return;
|
|
}
|
|
|
|
_zoomedIn = zoomed;
|
|
_zoomingIn = true;
|
|
_collectionView.userInteractionEnabled = false;
|
|
|
|
if (zoomed)
|
|
_pivotInItemIndex = index;
|
|
else
|
|
_pivotOutItemIndex = index;
|
|
|
|
UICollectionViewFlowLayout *toLayout = _zoomedIn ? _largeLayout : _smallLayout;
|
|
|
|
[self _updateImageSize];
|
|
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
TGTransitionLayout *layout = (TGTransitionLayout *)[_collectionView transitionToCollectionViewLayout:toLayout duration:0.3f completion:^(__unused BOOL completed, __unused BOOL finished)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_zoomingIn = false;
|
|
strongSelf->_collectionView.userInteractionEnabled = true;
|
|
[strongSelf centerOnItemWithIndex:index animated:false];
|
|
}];
|
|
layout.progressChanged = ^(CGFloat progress)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_zoomingProgress = progress;
|
|
[strongSelf requestMenuLayoutUpdate];
|
|
[strongSelf _layoutButtonItemViews];
|
|
[strongSelf setCameraZoomedIn:strongSelf->_zoomedIn progress:progress];
|
|
};
|
|
layout.transitionAlmostFinished = ^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_pivotInItemIndex = NSNotFound;
|
|
strongSelf->_pivotOutItemIndex = NSNotFound;
|
|
};
|
|
|
|
CGPoint toOffset = [_collectionView toContentOffsetForLayout:layout indexPath:[NSIndexPath indexPathForRow:index inSection:0] toSize:_collectionView.bounds.size toContentInset:[self collectionView:_collectionView layout:toLayout insetForSectionAtIndex:0]];
|
|
toOffset.y = 0;
|
|
layout.toContentOffset = toOffset;
|
|
|
|
for (TGMenuSheetItemView *itemView in self.underlyingViews)
|
|
[itemView setHidden:zoomed animated:animated];
|
|
|
|
[_sendMediaItemView setHidden:!zoomed animated:animated];
|
|
[_sendFileItemView setHidden:!zoomed animated:animated];
|
|
|
|
[self _updateVisibleItems];
|
|
}
|
|
|
|
- (void)updateSendButtonsFromIndex:(NSInteger)index
|
|
{
|
|
__block NSInteger photosCount = 0;
|
|
__block NSInteger videosCount = 0;
|
|
__block NSInteger gifsCount = 0;
|
|
|
|
[_selectionContext enumerateSelectedItems:^(id<TGMediaSelectableItem> item)
|
|
{
|
|
TGMediaAsset *asset = (TGMediaAsset *)item;
|
|
if (![asset isKindOfClass:[TGMediaAsset class]])
|
|
return;
|
|
|
|
switch (asset.type)
|
|
{
|
|
case TGMediaAssetVideoType:
|
|
videosCount++;
|
|
break;
|
|
|
|
case TGMediaAssetGifType:
|
|
gifsCount++;
|
|
break;
|
|
|
|
default:
|
|
photosCount++;
|
|
break;
|
|
}
|
|
}];
|
|
|
|
NSInteger totalCount = photosCount + videosCount + gifsCount;
|
|
bool activated = (totalCount > 0);
|
|
if ([self zoomedModeSupported])
|
|
[self setZoomedMode:activated animated:true index:index];
|
|
else
|
|
[self setSelectedMode:activated animated:true];
|
|
|
|
if (_document)
|
|
{
|
|
_sendMediaItemView.title = TGLocalized(@"Common.Done");
|
|
return;
|
|
}
|
|
|
|
if (totalCount == 0)
|
|
return;
|
|
|
|
if (photosCount > 0 && videosCount == 0 && gifsCount == 0)
|
|
{
|
|
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendPhoto_" value:photosCount]);
|
|
_sendMediaItemView.title = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", photosCount]];
|
|
}
|
|
else if (videosCount > 0 && photosCount == 0 && gifsCount == 0)
|
|
{
|
|
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendVideo_" value:videosCount]);
|
|
_sendMediaItemView.title = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", videosCount]];
|
|
}
|
|
else if (gifsCount > 0 && photosCount == 0 && videosCount == 0)
|
|
{
|
|
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendGif_" value:gifsCount]);
|
|
_sendMediaItemView.title = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", gifsCount]];
|
|
}
|
|
else
|
|
{
|
|
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendItem_" value:totalCount]);
|
|
_sendMediaItemView.title = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", totalCount]];
|
|
}
|
|
|
|
if (totalCount == 1)
|
|
_sendFileItemView.title = TGLocalized(@"AttachmentMenu.SendAsFile");
|
|
else
|
|
_sendFileItemView.title = TGLocalized(@"AttachmentMenu.SendAsFiles");
|
|
}
|
|
|
|
- (void)setSelectedMode:(bool)selected animated:(bool)animated
|
|
{
|
|
[self.underlyingViews.firstObject setHidden:selected animated:animated];
|
|
[_sendMediaItemView setHidden:!selected animated:animated];
|
|
}
|
|
|
|
- (bool)zoomedModeSupported
|
|
{
|
|
return [TGMediaAssetsLibrary usesPhotoFramework];
|
|
}
|
|
|
|
- (CGPoint)contentOffsetForItemAtIndex:(NSInteger)index
|
|
{
|
|
CGRect cellFrame = [_collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]].frame;
|
|
|
|
CGFloat x = cellFrame.origin.x - (_collectionView.frame.size.width - cellFrame.size.width) / 2.0f;
|
|
CGFloat contentOffset = MAX(0.0f, MIN(x, _collectionView.contentSize.width - _collectionView.frame.size.width));
|
|
|
|
return CGPointMake(contentOffset, 0);
|
|
}
|
|
|
|
- (void)centerOnItemWithIndex:(NSInteger)index animated:(bool)animated
|
|
{
|
|
[_collectionView setContentOffset:[self contentOffsetForItemAtIndex:index] animated:animated];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (CGFloat)_preferredHeightForZoomedIn:(bool)zoomedIn progress:(CGFloat)progress screenHeight:(CGFloat)__unused screenHeight
|
|
{
|
|
progress = zoomedIn ? progress : 1.0f - progress;
|
|
|
|
CGFloat inset = TGAttachmentEdgeInset * 2;
|
|
CGFloat cellHeight = TGAttachmentCellSize.height;
|
|
CGFloat targetCellHeight = _smallActivated ? _smallMaxPhotoSize.height : _maxPhotoSize.height;
|
|
|
|
cellHeight = cellHeight + (targetCellHeight - cellHeight) * progress;
|
|
|
|
return cellHeight + inset;
|
|
}
|
|
|
|
- (CGFloat)_heightCorrectionForZoomedIn:(bool)zoomedIn progress:(CGFloat)progress
|
|
{
|
|
progress = zoomedIn ? progress : 1.0f - progress;
|
|
|
|
CGFloat correction = self.remainingHeight - (_document ? 1 : 2) * TGMenuSheetButtonItemViewHeight;
|
|
return -(correction * progress);
|
|
}
|
|
|
|
- (CGFloat)preferredHeightForWidth:(CGFloat)__unused width screenHeight:(CGFloat)screenHeight
|
|
{
|
|
if (_collapsed) {
|
|
self.alpha = 0.0f;
|
|
return 0.0f;
|
|
}
|
|
|
|
CGFloat progress = _zoomingIn ? _zoomingProgress : 1.0f;
|
|
return [self _preferredHeightForZoomedIn:_zoomedIn progress:progress screenHeight:screenHeight];
|
|
}
|
|
|
|
- (CGFloat)contentHeightCorrection
|
|
{
|
|
CGFloat progress = _zoomingIn ? _zoomingProgress : 1.0f;
|
|
return [self _heightCorrectionForZoomedIn:_zoomedIn progress:progress];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
|
{
|
|
if (!_sendMediaItemView.userInteractionEnabled)
|
|
return [super pointInside:point withEvent:event];
|
|
|
|
return CGRectContainsPoint(self.bounds, point) || CGRectContainsPoint(_sendMediaItemView.frame, point) || CGRectContainsPoint(_sendFileItemView.frame, point);
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)_updateVisibleItems
|
|
{
|
|
for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems)
|
|
{
|
|
TGMediaAsset *asset = [_fetchResult assetAtIndex:indexPath.row];
|
|
TGAttachmentAssetCell *cell = (TGAttachmentAssetCell *)[_collectionView cellForItemAtIndexPath:indexPath];
|
|
if (cell.isZoomed != _zoomedIn)
|
|
{
|
|
cell.isZoomed = _zoomedIn;
|
|
if (cell.asset == nil)
|
|
[cell setAsset:asset signal:[self _signalForItem:asset refresh:true onlyThumbnail:false]];
|
|
else
|
|
[cell setSignal:[self _signalForItem:asset refresh:true onlyThumbnail:false]];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)_updateImageSize
|
|
{
|
|
_imageSize = [self imageSizeForThumbnail:!_zoomedIn];
|
|
}
|
|
|
|
- (CGSize)imageSizeForThumbnail:(bool)forThumbnail
|
|
{
|
|
CGFloat scale = MIN(2.0f, TGScreenScaling());
|
|
if (forThumbnail)
|
|
return CGSizeMake(TGAttachmentCellSize.width * scale, TGAttachmentCellSize.height * scale);
|
|
else
|
|
return CGSizeMake(floor(TGAttachmentZoomedPhotoMaxWidth * scale), floor(TGAttachmentZoomedPhotoMaxWidth * scale));
|
|
}
|
|
|
|
- (bool)hasCameraInCurrentMode
|
|
{
|
|
return (!_zoomedIn && _cameraView != nil);
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)_setupGalleryMixin:(TGMediaPickerModernGalleryMixin *)mixin
|
|
{
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
mixin.referenceViewForItem = ^UIView *(TGMediaPickerGalleryItem *item)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
return [strongSelf referenceViewForAsset:item.asset];
|
|
|
|
return nil;
|
|
};
|
|
|
|
mixin.itemFocused = ^(TGMediaPickerGalleryItem *item)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_hiddenItem = item.asset;
|
|
[strongSelf updateHiddenCellAnimated:false];
|
|
};
|
|
|
|
mixin.willTransitionIn = ^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf.superview bringSubviewToFront:strongSelf];
|
|
[strongSelf->_cameraView pausePreview];
|
|
};
|
|
|
|
mixin.willTransitionOut = ^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf->_cameraView resumePreview];
|
|
};
|
|
|
|
mixin.didTransitionOut = ^
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_hiddenItem = nil;
|
|
[strongSelf updateHiddenCellAnimated:true];
|
|
|
|
strongSelf->_galleryMixin = nil;
|
|
};
|
|
|
|
mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
|
{
|
|
if (strongSelf->_selectionContext.allowGrouping)
|
|
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
|
strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime);
|
|
}
|
|
};
|
|
|
|
mixin.editorOpened = self.editorOpened;
|
|
mixin.editorClosed = self.editorClosed;
|
|
}
|
|
|
|
- (TGMediaPickerModernGalleryMixin *)galleryMixinForIndexPath:(NSIndexPath *)indexPath previewMode:(bool)previewMode outAsset:(TGMediaAsset **)outAsset
|
|
{
|
|
TGMediaAsset *asset = [_fetchResult assetAtIndex:indexPath.row];
|
|
if (outAsset != NULL)
|
|
*outAsset = asset;
|
|
|
|
UIImage *thumbnailImage = nil;
|
|
|
|
TGAttachmentAssetCell *cell = (TGAttachmentAssetCell *)[_collectionView cellForItemAtIndexPath:indexPath];
|
|
if ([cell isKindOfClass:[TGAttachmentAssetCell class]])
|
|
thumbnailImage = cell.imageView.image;
|
|
|
|
TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext suggestionContext:self.suggestionContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder stickersContext:self.stickersContext];
|
|
mixin.presentScheduleController = self.presentScheduleController;
|
|
mixin.presentTimerController = self.presentTimerController;
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
mixin.thumbnailSignalForItem = ^SSignal *(id item)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return nil;
|
|
|
|
return [strongSelf _signalForItem:item refresh:false onlyThumbnail:true];
|
|
};
|
|
|
|
if (!previewMode)
|
|
[self _setupGalleryMixin:mixin];
|
|
|
|
return mixin;
|
|
}
|
|
|
|
- (UIView *)referenceViewForAsset:(TGMediaAsset *)asset
|
|
{
|
|
for (TGAttachmentAssetCell *cell in [_collectionView visibleCells])
|
|
{
|
|
if ([cell.asset.uniqueIdentifier isEqual:asset.uniqueIdentifier])
|
|
return cell;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
NSInteger index = indexPath.row;
|
|
TGMediaAsset *asset = [_fetchResult assetAtIndex:index];
|
|
|
|
__block UIImage *thumbnailImage = nil;
|
|
if ([TGMediaAssetsLibrary usesPhotoFramework])
|
|
{
|
|
TGAttachmentAssetCell *cell = (TGAttachmentAssetCell *)[collectionView cellForItemAtIndexPath:indexPath];
|
|
if ([cell isKindOfClass:[TGAttachmentAssetCell class]])
|
|
thumbnailImage = cell.imageView.image;
|
|
}
|
|
else
|
|
{
|
|
[[TGMediaAssetImageSignals imageForAsset:asset imageType:TGMediaAssetImageTypeAspectRatioThumbnail size:CGSizeZero] startWithNext:^(UIImage *next)
|
|
{
|
|
thumbnailImage = next;
|
|
}];
|
|
}
|
|
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
UIView *(^referenceViewForAsset)(TGMediaAsset *) = ^UIView *(TGMediaAsset *asset)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
return [strongSelf referenceViewForAsset:asset];
|
|
|
|
return nil;
|
|
};
|
|
|
|
if (self.openEditor)
|
|
{
|
|
id<LegacyComponentsOverlayWindowManager> windowManager = [_context makeOverlayWindowManager];
|
|
|
|
id<TGMediaEditableItem> editableItem = asset;
|
|
if (asset.type == TGMediaAssetGifType) {
|
|
editableItem = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:false];
|
|
}
|
|
|
|
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:_disableStickers ? TGPhotoEditorControllerSignupAvatarIntent : TGPhotoEditorControllerAvatarIntent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
|
controller.editingContext = _editingContext;
|
|
controller.stickersContext = _stickersContext;
|
|
controller.dontHideStatusBar = true;
|
|
|
|
TGMediaAvatarEditorTransition *transition = [[TGMediaAvatarEditorTransition alloc] initWithController:controller fromView:referenceViewForAsset(asset)];
|
|
|
|
controller.didFinishRenderingFullSizeImage = ^(UIImage *resultImage)
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil || !strongSelf->_saveEditedPhotos)
|
|
return;
|
|
|
|
[[strongSelf->_assetsLibrary saveAssetWithImage:resultImage] startWithNext:nil];
|
|
};
|
|
|
|
__weak TGPhotoEditorController *weakController = controller;
|
|
controller.didFinishEditing = ^(__unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges)
|
|
{
|
|
if (!hasChanges)
|
|
return;
|
|
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
__strong TGPhotoEditorController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
if (strongSelf.avatarCompletionBlock != nil)
|
|
strongSelf.avatarCompletionBlock(resultImage);
|
|
};
|
|
controller.didFinishEditingVideo = ^(NSURL *url, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges) {
|
|
if (!hasChanges)
|
|
return;
|
|
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
__strong TGPhotoEditorController *strongController = weakController;
|
|
if (strongController == nil)
|
|
return;
|
|
|
|
if (strongSelf.avatarVideoCompletionBlock != nil)
|
|
strongSelf.avatarVideoCompletionBlock(resultImage, url, adjustments);
|
|
};
|
|
controller.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
|
|
{
|
|
return [editableItem thumbnailImageSignal];
|
|
};
|
|
|
|
controller.requestOriginalScreenSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
|
{
|
|
return [editableItem screenImageSignal:position];
|
|
};
|
|
|
|
controller.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
|
{
|
|
if (editableItem.isVideo) {
|
|
if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
|
|
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
|
|
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
|
|
return ((TGCameraCapturedVideo *)editableItem).avAsset;
|
|
} else {
|
|
return [editableItem originalImageSignal:position];
|
|
}
|
|
} else {
|
|
return [editableItem originalImageSignal:position];
|
|
}
|
|
};
|
|
|
|
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:_parentController contentController:controller];
|
|
controllerWindow.hidden = false;
|
|
controller.view.clipsToBounds = true;
|
|
|
|
transition.referenceFrame = ^CGRect
|
|
{
|
|
UIView *referenceView = referenceViewForAsset(asset);
|
|
return [referenceView.superview convertRect:referenceView.frame toView:nil];
|
|
};
|
|
transition.referenceImageSize = ^CGSize
|
|
{
|
|
return asset.dimensions;
|
|
};
|
|
transition.referenceScreenImageSignal = ^SSignal *
|
|
{
|
|
return [TGMediaAssetImageSignals imageForAsset:asset imageType:TGMediaAssetImageTypeFastScreen size:CGSizeMake(640, 640)];
|
|
};
|
|
[transition presentAnimated:true];
|
|
|
|
controller.beginCustomTransitionOut = ^(CGRect outReferenceFrame, UIView *repView, void (^completion)(void))
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
transition.outReferenceFrame = outReferenceFrame;
|
|
transition.repView = repView;
|
|
[transition dismissAnimated:true completion:^
|
|
{
|
|
strongSelf->_hiddenItem = nil;
|
|
[strongSelf updateHiddenCellAnimated:true];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
if (completion != nil)
|
|
completion();
|
|
});
|
|
}];
|
|
};
|
|
|
|
_hiddenItem = asset;
|
|
[self updateHiddenCellAnimated:false];
|
|
}
|
|
else
|
|
{
|
|
_galleryMixin = [self galleryMixinForIndexPath:indexPath previewMode:false outAsset:NULL];
|
|
[_galleryMixin present];
|
|
}
|
|
}
|
|
|
|
- (NSInteger)collectionView:(UICollectionView *)__unused collectionView numberOfItemsInSection:(NSInteger)__unused section
|
|
{
|
|
return MIN(_fetchResult.count, TGAttachmentDisplayedAssetLimit);
|
|
}
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
NSInteger index = indexPath.row;
|
|
|
|
TGMediaAsset *asset = [_fetchResult assetAtIndex:index];
|
|
NSString *cellIdentifier = nil;
|
|
switch (asset.type)
|
|
{
|
|
case TGMediaAssetVideoType:
|
|
cellIdentifier = TGAttachmentVideoCellIdentifier;
|
|
break;
|
|
|
|
case TGMediaAssetGifType:
|
|
cellIdentifier = TGAttachmentGifCellIdentifier;
|
|
break;
|
|
|
|
default:
|
|
cellIdentifier = TGAttachmentPhotoCellIdentifier;
|
|
break;
|
|
}
|
|
|
|
TGAttachmentAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
|
|
cell.pallete = _pallete;
|
|
NSInteger pivotIndex = NSNotFound;
|
|
NSInteger limit = 0;
|
|
if (_pivotInItemIndex != NSNotFound)
|
|
{
|
|
if (self.frame.size.width <= 320)
|
|
limit = 2;
|
|
else
|
|
limit = 3;
|
|
|
|
pivotIndex = _pivotInItemIndex;
|
|
}
|
|
else if (_pivotOutItemIndex != NSNotFound)
|
|
{
|
|
pivotIndex = _pivotOutItemIndex;
|
|
|
|
if (self.frame.size.width <= 320)
|
|
limit = 4;
|
|
else
|
|
limit = 6;
|
|
}
|
|
|
|
cell.selectionContext = _selectionContext;
|
|
cell.editingContext = _editingContext;
|
|
|
|
if (!(pivotIndex != NSNotFound && (indexPath.row < pivotIndex - limit || indexPath.row > pivotIndex + limit)))
|
|
{
|
|
if (![asset isEqual:cell.asset] || cell.isZoomed != _zoomedIn)
|
|
{
|
|
cell.isZoomed = _zoomedIn;
|
|
[cell setAsset:asset signal:[self _signalForItem:asset refresh:[cell.asset isEqual:asset] onlyThumbnail:false]];
|
|
}
|
|
}
|
|
|
|
if (self.selectionContext != nil)
|
|
[cell.checkButton setNumber:[self.selectionContext indexOfItem:asset]];
|
|
|
|
return cell;
|
|
}
|
|
|
|
- (void)updateHiddenCellAnimated:(bool)animated
|
|
{
|
|
for (TGAttachmentAssetCell *cell in [_collectionView visibleCells])
|
|
[cell setHidden:([cell.asset.uniqueIdentifier isEqual:_hiddenItem.uniqueIdentifier]) animated:animated];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (CGSize)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (_zoomedIn)
|
|
{
|
|
CGSize maxPhotoSize = _maxPhotoSize;
|
|
if (_smallActivated)
|
|
maxPhotoSize = _smallMaxPhotoSize;
|
|
|
|
if (_pivotInItemIndex != NSNotFound && (indexPath.row < _pivotInItemIndex - 2 || indexPath.row > _pivotInItemIndex + 2))
|
|
return CGSizeMake(maxPhotoSize.height, maxPhotoSize.height);
|
|
|
|
TGMediaAsset *asset = [_fetchResult assetAtIndex:indexPath.row];
|
|
if (asset != nil)
|
|
{
|
|
CGSize dimensions = asset.dimensions;
|
|
if (dimensions.width < 1.0f)
|
|
dimensions.width = 1.0f;
|
|
if (dimensions.height < 1.0f)
|
|
dimensions.height = 1.0f;
|
|
|
|
id<TGMediaEditAdjustments> adjustments = [_editingContext adjustmentsForItem:asset];
|
|
if ([adjustments cropAppliedForAvatar:false])
|
|
{
|
|
dimensions = adjustments.cropRect.size;
|
|
|
|
bool sideward = TGOrientationIsSideward(adjustments.cropOrientation, NULL);
|
|
if (sideward)
|
|
dimensions = CGSizeMake(dimensions.height, dimensions.width);
|
|
}
|
|
|
|
CGFloat width = MIN(maxPhotoSize.width, ceil(dimensions.width * maxPhotoSize.height / dimensions.height));
|
|
return CGSizeMake(width, maxPhotoSize.height);
|
|
}
|
|
|
|
return CGSizeMake(maxPhotoSize.height, maxPhotoSize.height);
|
|
}
|
|
|
|
return TGAttachmentCellSize;
|
|
}
|
|
|
|
- (UIEdgeInsets)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)__unused section
|
|
{
|
|
CGFloat edgeInset = TGAttachmentEdgeInset;
|
|
CGFloat leftInset = [self hasCameraInCurrentMode] ? 2 * edgeInset + 84.0f : edgeInset;
|
|
|
|
CGFloat height = self.frame.size.height;
|
|
|
|
if (collectionViewLayout == _smallLayout)
|
|
height = [self _preferredHeightForZoomedIn:false progress:1.0f screenHeight:self.screenHeight];
|
|
else if (collectionViewLayout == _largeLayout)
|
|
height = [self _preferredHeightForZoomedIn:true progress:1.0f screenHeight:self.screenHeight];
|
|
|
|
CGFloat cellHeight = height - 2 * edgeInset;
|
|
CGFloat topInset = _collectionView.frame.size.height - cellHeight - edgeInset;
|
|
CGFloat bottomInset = edgeInset;
|
|
|
|
return UIEdgeInsetsMake(topInset, leftInset, bottomInset, edgeInset);
|
|
}
|
|
|
|
- (CGFloat)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)__unused section
|
|
{
|
|
return _smallLayout.minimumLineSpacing;
|
|
}
|
|
|
|
- (UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)__unused collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout
|
|
{
|
|
return [[TGTransitionLayout alloc] initWithCurrentLayout:fromLayout nextLayout:toLayout];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)scrollViewDidScroll:(UIScrollView *)__unused scrollView
|
|
{
|
|
if (_zoomingIn)
|
|
return;
|
|
|
|
if (!_zoomedIn)
|
|
[_preheatMixin update];
|
|
|
|
for (UICollectionViewCell *cell in _collectionView.visibleCells)
|
|
{
|
|
if ([cell isKindOfClass:[TGAttachmentAssetCell class]])
|
|
[(TGAttachmentAssetCell *)cell setNeedsLayout];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)menuView:(TGMenuSheetView *)menuView willAppearAnimated:(bool)__unused animated
|
|
{
|
|
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
|
menuView.tapDismissalAllowed = ^bool
|
|
{
|
|
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return true;
|
|
|
|
return !strongSelf->_collectionView.isDecelerating && !strongSelf->_collectionView.isTracking;
|
|
};
|
|
}
|
|
|
|
- (void)menuView:(TGMenuSheetView *)menuView willDisappearAnimated:(bool)animated
|
|
{
|
|
[super menuView:menuView didDisappearAnimated:animated];
|
|
menuView.tapDismissalAllowed = nil;
|
|
[_cameraView stopPreview];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)setScreenHeight:(CGFloat)screenHeight
|
|
{
|
|
_screenHeight = screenHeight;
|
|
[self _updateSmallActivated];
|
|
|
|
}
|
|
|
|
- (void)setSizeClass:(UIUserInterfaceSizeClass)sizeClass
|
|
{
|
|
_sizeClass = sizeClass;
|
|
[self _updateSmallActivated];
|
|
}
|
|
|
|
- (void)_updateSmallActivated
|
|
{
|
|
_smallActivated = (fabs(_screenHeight - _smallActivationHeight) < FLT_EPSILON && _sizeClass == UIUserInterfaceSizeClassCompact);
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
[super layoutSubviews];
|
|
|
|
CGRect frame = _collectionView.frame;
|
|
frame.size.width = self.frame.size.width;
|
|
|
|
frame.size.height = (_smallActivated ? _smallMaxPhotoSize.height : _maxPhotoSize.height) + TGAttachmentEdgeInset * 2;
|
|
frame.origin.y = self.frame.size.height - frame.size.height;
|
|
|
|
if (!CGRectEqualToRect(frame, _collectionView.frame))
|
|
{
|
|
bool invalidate = fabs(_collectionView.frame.size.height - frame.size.height) > FLT_EPSILON;
|
|
|
|
_collectionView.frame = frame;
|
|
|
|
if (invalidate)
|
|
{
|
|
[_smallLayout invalidateLayout];
|
|
[_largeLayout invalidateLayout];
|
|
[_collectionView layoutSubviews];
|
|
}
|
|
}
|
|
|
|
CGFloat height = self.frame.size.height;
|
|
CGFloat cellHeight = height - 2 * TGAttachmentEdgeInset;
|
|
CGFloat topInset = _collectionView.frame.size.height - cellHeight - TGAttachmentEdgeInset;
|
|
|
|
frame = _cameraView.frame;
|
|
frame.origin.y = topInset;
|
|
_cameraView.frame = frame;
|
|
|
|
[self _layoutButtonItemViews];
|
|
}
|
|
|
|
- (void)_layoutButtonItemViews
|
|
{
|
|
_sendMediaItemView.frame = CGRectMake(0, [self preferredHeightForWidth:self.frame.size.width screenHeight:self.screenHeight], self.frame.size.width, [_sendMediaItemView preferredHeightForWidth:self.frame.size.width screenHeight:self.screenHeight]);
|
|
_sendFileItemView.frame = CGRectMake(0, CGRectGetMaxY(_sendMediaItemView.frame), self.frame.size.width, [_sendFileItemView preferredHeightForWidth:self.frame.size.width screenHeight:self.screenHeight]);
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (UIView *)previewSourceView
|
|
{
|
|
return _collectionView;
|
|
}
|
|
|
|
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location
|
|
{
|
|
if (self.openEditor) {
|
|
return nil;
|
|
}
|
|
|
|
NSIndexPath *indexPath = [_collectionView indexPathForItemAtPoint:location];
|
|
if (indexPath == nil)
|
|
return nil;
|
|
|
|
CGRect cellFrame = [_collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath].frame;
|
|
previewingContext.sourceRect = cellFrame;
|
|
|
|
TGMediaAsset *asset = nil;
|
|
_previewGalleryMixin = [self galleryMixinForIndexPath:indexPath previewMode:true outAsset:&asset];
|
|
UIViewController *controller = [_previewGalleryMixin galleryController];
|
|
|
|
CGSize screenSize = TGScreenSize();
|
|
controller.preferredContentSize = TGFitSize(asset.dimensions, screenSize);
|
|
[_previewGalleryMixin setPreviewMode];
|
|
return controller;
|
|
}
|
|
|
|
- (void)previewingContext:(id<UIViewControllerPreviewing>)__unused previewingContext commitViewController:(UIViewController *)__unused viewControllerToCommit
|
|
{
|
|
_galleryMixin = _previewGalleryMixin;
|
|
_previewGalleryMixin = nil;
|
|
|
|
[self _setupGalleryMixin:_galleryMixin];
|
|
[_galleryMixin present];
|
|
}
|
|
|
|
@end
|