mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-31 07:30:40 +00:00
Merge commit '5e36741afa9f5f28e55af8fd13e718a300c98870'
This commit is contained in:
commit
9cd7e3a8f1
@ -312,6 +312,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
}
|
||||
}
|
||||
|
||||
public var updateOnReplacement = false
|
||||
public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, synchronous: Bool = false) {
|
||||
var updateItems: [GalleryPagerUpdateItem] = []
|
||||
var deleteItems: [Int] = []
|
||||
@ -326,14 +327,20 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 ..< items.count {
|
||||
if i == previousIndexById[items[i].id] {
|
||||
updateItems.append(GalleryPagerUpdateItem(index: i, previousIndex: i, item: items[i]))
|
||||
} else {
|
||||
if self.updateOnReplacement {
|
||||
for i in 0 ..< items.count {
|
||||
if (previousIndexById[items[i].id] == nil) {
|
||||
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
|
||||
} else {
|
||||
updateItems.append(GalleryPagerUpdateItem(index: i, previousIndex: i, item: items[i]))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i in 0 ..< items.count {
|
||||
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous))
|
||||
}
|
||||
|
||||
@ -404,7 +411,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
self.ignoreCentralItemIndexUpdate = true
|
||||
self.centralItemIndex = focusOnItem
|
||||
self.ignoreCentralItemIndexUpdate = false
|
||||
self.updateItemNodes(transition: .immediate, forceOffsetReset: true)
|
||||
self.updateItemNodes(transition: .immediate, forceOffsetReset: true, synchronous: transaction.synchronous)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,8 @@
|
||||
- (void)closeCurtains;
|
||||
- (void)openCurtains;
|
||||
|
||||
- (void)flash:(void (^)(void))completion;
|
||||
|
||||
- (void)invalidateCropRect;
|
||||
|
||||
- (UIImage *)currentImage;
|
||||
|
@ -44,6 +44,7 @@
|
||||
- (void)prepareTransitionOutSaving:(bool)saving;
|
||||
|
||||
- (void)prepareForCustomTransitionOut;
|
||||
- (void)finishCustomTransitionOut;
|
||||
|
||||
- (void)animateTransitionIn;
|
||||
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame;
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
@interface TGPhotoVideoEditor : NSObject
|
||||
|
||||
+ (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo;
|
||||
|
||||
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
|
||||
|
||||
@end
|
||||
|
@ -484,7 +484,6 @@
|
||||
[_finalFilter addTarget:self.previewOutput.imageView];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_histogramGenerator != nil && !self.standalone) {
|
||||
[_finalFilter addTarget:_histogramGenerator];
|
||||
|
@ -23,8 +23,12 @@
|
||||
|
||||
#import <LegacyComponents/TGMenuView.h>
|
||||
|
||||
#import "TGPaintFaceDetector.h"
|
||||
|
||||
@interface TGMediaPickerGalleryPhotoItemView ()
|
||||
{
|
||||
SMetaDisposable *_facesDisposable;
|
||||
|
||||
UILabel *_fileInfoLabel;
|
||||
|
||||
TGMessageImageViewOverlayView *_progressView;
|
||||
@ -57,6 +61,8 @@
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_facesDisposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
__weak TGMediaPickerGalleryPhotoItemView *weakSelf = self;
|
||||
_imageView = [[TGModernGalleryImageItemImageView alloc] init];
|
||||
_imageView.clipsToBounds = true;
|
||||
@ -101,6 +107,7 @@
|
||||
{
|
||||
[_adjustmentsDisposable dispose];
|
||||
[_attributesDisposable dispose];
|
||||
[_facesDisposable dispose];
|
||||
}
|
||||
|
||||
- (void)setHiddenAsBeingEdited:(bool)hidden
|
||||
@ -226,8 +233,11 @@
|
||||
|
||||
}]];
|
||||
|
||||
if (!item.asFile)
|
||||
if (!item.asFile) {
|
||||
[_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startWithNext:nil]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_fileInfoLabel.text = nil;
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#import <LegacyComponents/TGMenuView.h>
|
||||
|
||||
#import "PGPhotoEditor.h"
|
||||
#import "TGPaintFaceDetector.h"
|
||||
|
||||
@interface TGMediaPickerGalleryVideoItemView() <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate>
|
||||
{
|
||||
@ -96,6 +97,7 @@
|
||||
SMetaDisposable *_adjustmentsDisposable;
|
||||
SMetaDisposable *_attributesDisposable;
|
||||
SMetaDisposable *_downloadDisposable;
|
||||
SMetaDisposable *_facesDisposable;
|
||||
SMetaDisposable *_currentAudioSession;
|
||||
|
||||
SVariable *_editableItemVariable;
|
||||
@ -131,6 +133,7 @@
|
||||
|
||||
_currentAudioSession = [[SMetaDisposable alloc] init];
|
||||
_playerItemDisposable = [[SMetaDisposable alloc] init];
|
||||
_facesDisposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
_videoDurationVar = [[SVariable alloc] init];
|
||||
_videoDurationDisposable = [[SMetaDisposable alloc] init];
|
||||
@ -251,6 +254,7 @@
|
||||
[_thumbnailsDisposable dispose];
|
||||
[_attributesDisposable dispose];
|
||||
[_downloadDisposable dispose];
|
||||
[_facesDisposable dispose];
|
||||
[self stopPlayer];
|
||||
|
||||
[self releaseVolumeOverlay];
|
||||
@ -385,8 +389,13 @@
|
||||
|
||||
[super setItem:item synchronously:synchronously];
|
||||
|
||||
if (itemChanged)
|
||||
if (itemChanged) {
|
||||
[self _playerCleanup];
|
||||
|
||||
if (!item.asFile) {
|
||||
[_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startWithNext:nil]];
|
||||
}
|
||||
}
|
||||
|
||||
_scrubberView.allowsTrimming = false;
|
||||
_videoDimensions = item.dimensions;
|
||||
|
@ -1,6 +1,9 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
@protocol TGMediaEditableItem;
|
||||
@class TGMediaEditingContext;
|
||||
|
||||
@interface TGPaintFaceFeature : NSObject
|
||||
{
|
||||
CGPoint _position;
|
||||
@ -49,6 +52,8 @@
|
||||
|
||||
+ (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize;
|
||||
|
||||
+ (SSignal *)detectFacesInItem:(id<TGMediaEditableItem>)item editingContext:(TGMediaEditingContext *)editingContext;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -3,6 +3,9 @@
|
||||
#import <LegacyComponents/TGPaintUtils.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
#import "TGMediaEditingContext.h"
|
||||
#import "UIImage+TG.h"
|
||||
|
||||
@interface TGPaintFace ()
|
||||
|
||||
+ (instancetype)faceWithBounds:(CGRect)bounds angle:(CGFloat)angle leftEye:(TGPaintFaceEye *)leftEye rightEye:(TGPaintFaceEye *)rightEye mouth:(TGPaintFaceMouth *)mouth;
|
||||
@ -26,6 +29,41 @@
|
||||
|
||||
@implementation TGPaintFaceDetector
|
||||
|
||||
+ (SSignal *)detectFacesInItem:(id<TGMediaEditableItem>)item editingContext:(TGMediaEditingContext *)editingContext
|
||||
{
|
||||
CGSize originalSize = item.originalSize;
|
||||
|
||||
SSignal *cachedFaces = [editingContext facesForItem:item];
|
||||
SSignal *cachedSignal = [cachedFaces mapToSignal:^SSignal *(id result)
|
||||
{
|
||||
if (result == nil)
|
||||
return [SSignal fail:nil];
|
||||
return [SSignal single:result];
|
||||
}];
|
||||
|
||||
SSignal *imageSignal = [item screenImageSignal:0];
|
||||
SSignal *detectSignal = [[[imageSignal filter:^bool(UIImage *image)
|
||||
{
|
||||
if (![image isKindOfClass:[UIImage class]])
|
||||
return false;
|
||||
|
||||
if (image.degraded)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}] take:1] mapToSignal:^SSignal *(UIImage *image) {
|
||||
return [[TGPaintFaceDetector detectFacesInImage:image originalSize:originalSize] startOn:[SQueue concurrentDefaultQueue]];
|
||||
}];
|
||||
|
||||
return [[[cachedSignal catch:^SSignal *(__unused id error)
|
||||
{
|
||||
return detectSignal;
|
||||
}] deliverOn:[SQueue mainQueue]] onNext:^(NSArray *next)
|
||||
{
|
||||
[editingContext setFaces:next forItem:item];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize
|
||||
{
|
||||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||
|
@ -26,6 +26,8 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
UIView *_snapshotView;
|
||||
CGSize _snapshotSize;
|
||||
|
||||
UIView *_flashView;
|
||||
|
||||
UIView *_topOverlayView;
|
||||
UIView *_leftOverlayView;
|
||||
UIView *_rightOverlayView;
|
||||
@ -39,7 +41,7 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
|
||||
CGFloat _currentDiameter;
|
||||
|
||||
PGPhotoEditorView *_fullPreviewView;
|
||||
__weak PGPhotoEditorView *_fullPreviewView;
|
||||
}
|
||||
@end
|
||||
|
||||
@ -82,6 +84,12 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
_fullPreviewView.userInteractionEnabled = false;
|
||||
[_wrapperView addSubview:_fullPreviewView];
|
||||
|
||||
_flashView = [[UIView alloc] init];
|
||||
_flashView.alpha = 0.0;
|
||||
_flashView.backgroundColor = [UIColor whiteColor];
|
||||
_flashView.userInteractionEnabled = false;
|
||||
[self addSubview:_flashView];
|
||||
|
||||
_topCurtainView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_topCurtainView.backgroundColor = [UIColor blackColor];
|
||||
[self addSubview:_topCurtainView];
|
||||
@ -122,6 +130,11 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_scrollView.delegate = nil;
|
||||
}
|
||||
|
||||
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
|
||||
if (self.tapped != nil)
|
||||
self.tapped();
|
||||
@ -611,6 +624,8 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
{
|
||||
[self _layoutOverlayViews];
|
||||
|
||||
_flashView.frame = self.bounds;
|
||||
|
||||
if (_scrollView.superview == nil)
|
||||
{
|
||||
_scrollView.frame = self.bounds;
|
||||
@ -647,4 +662,16 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
return CGSizeMake(20, 20);
|
||||
}
|
||||
|
||||
- (void)flash:(void (^)(void))completion {
|
||||
[UIView animateWithDuration:0.12 animations:^{
|
||||
_flashView.alpha = 1.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_flashView.alpha = 0.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
completion();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -15,7 +15,12 @@
|
||||
@property (nonatomic, copy) void (^croppingChanged)(void);
|
||||
@property (nonatomic, copy) void (^togglePlayback)(void);
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIView *)dotImageView fullPreviewView:(PGPhotoEditorView *)fullPreviewView;
|
||||
@property (nonatomic, weak) UIView *dotImageView;
|
||||
@property (nonatomic, weak) UIView *dotMarkerView;
|
||||
@property (nonatomic, weak) PGPhotoEditorView *fullPreviewView;
|
||||
@property (nonatomic, weak) TGMediaPickerGalleryVideoScrubber *scrubberView;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView;
|
||||
|
||||
- (void)setImage:(UIImage *)image;
|
||||
- (void)setSnapshotImage:(UIImage *)snapshotImage;
|
||||
|
@ -32,18 +32,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
UIView *_wrapperView;
|
||||
|
||||
TGPhotoAvatarCropView *_cropView;
|
||||
PGPhotoEditorView *_fullPreviewView;
|
||||
__weak TGPhotoAvatarCropView *_cropView;
|
||||
|
||||
UIView *_portraitToolsWrapperView;
|
||||
UIView *_landscapeToolsWrapperView;
|
||||
UIView *_portraitWrapperBackgroundView;
|
||||
UIView *_landscapeWrapperBackgroundView;
|
||||
|
||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||
UIView *_dotImageView;
|
||||
UIView *_videoAreaView;
|
||||
UIView *_flashView;
|
||||
|
||||
UIView *_portraitToolControlView;
|
||||
UIView *_landscapeToolControlView;
|
||||
UILabel *_coverLabel;
|
||||
@ -58,21 +53,20 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
@implementation TGPhotoAvatarPreviewController
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIView *)dotImageView fullPreviewView:(PGPhotoEditorView *)fullPreviewView
|
||||
{
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView {
|
||||
self = [super initWithContext:context];
|
||||
if (self != nil)
|
||||
{
|
||||
self.photoEditor = photoEditor;
|
||||
self.previewView = previewView;
|
||||
_fullPreviewView = fullPreviewView;
|
||||
_scrubberView = scrubberView;
|
||||
|
||||
_dotImageView = dotImageView;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
NSLog(@"");
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
@ -90,7 +84,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
self.controlVideoPlayback(false);
|
||||
strongSelf.controlVideoPlayback(false);
|
||||
};
|
||||
void(^interactionEnded)(void) = ^
|
||||
{
|
||||
@ -101,11 +95,12 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
if ([strongSelf shouldAutorotate])
|
||||
[TGViewController attemptAutorotation];
|
||||
|
||||
self.controlVideoPlayback(true);
|
||||
strongSelf.controlVideoPlayback(true);
|
||||
};
|
||||
|
||||
PGPhotoEditor *photoEditor = self.photoEditor;
|
||||
_cropView = [[TGPhotoAvatarCropView alloc] initWithOriginalSize:photoEditor.originalSize screenSize:[self referenceViewSize] fullPreviewView:_fullPreviewView];
|
||||
TGPhotoAvatarCropView *cropView = [[TGPhotoAvatarCropView alloc] initWithOriginalSize:photoEditor.originalSize screenSize:[self referenceViewSize] fullPreviewView:_fullPreviewView];
|
||||
_cropView = cropView;
|
||||
[_cropView setCropRect:photoEditor.cropRect];
|
||||
[_cropView setCropOrientation:photoEditor.cropOrientation];
|
||||
[_cropView setCropMirrored:photoEditor.cropMirrored];
|
||||
@ -142,7 +137,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
}
|
||||
_cropView.interactionBegan = interactionBegan;
|
||||
_cropView.interactionEnded = interactionEnded;
|
||||
[_wrapperView addSubview:_cropView];
|
||||
[_wrapperView addSubview:cropView];
|
||||
|
||||
_portraitToolsWrapperView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
[_wrapperView addSubview:_portraitToolsWrapperView];
|
||||
@ -162,18 +157,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
_landscapeWrapperBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
|
||||
_landscapeWrapperBackgroundView.userInteractionEnabled = false;
|
||||
[_landscapeToolsWrapperView addSubview:_landscapeWrapperBackgroundView];
|
||||
|
||||
_videoAreaView = [[UIView alloc] init];
|
||||
[self.view insertSubview:_videoAreaView belowSubview:_wrapperView];
|
||||
|
||||
|
||||
[_portraitToolsWrapperView addSubview:_scrubberView];
|
||||
|
||||
_flashView = [[UIView alloc] init];
|
||||
_flashView.alpha = 0.0;
|
||||
_flashView.backgroundColor = [UIColor whiteColor];
|
||||
_flashView.userInteractionEnabled = false;
|
||||
[_videoAreaView addSubview:_flashView];
|
||||
|
||||
_coverLabel = [[UILabel alloc] init];
|
||||
_coverLabel.alpha = 0.7f;
|
||||
_coverLabel.backgroundColor = [UIColor clearColor];
|
||||
@ -183,7 +169,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
[_coverLabel sizeToFit];
|
||||
[_portraitToolsWrapperView addSubview:_coverLabel];
|
||||
|
||||
_dotImageView.alpha = 1.0f;
|
||||
[_wrapperView addSubview:_dotImageView];
|
||||
}
|
||||
}
|
||||
@ -263,7 +248,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
- (void)prepareTransitionInWithReferenceView:(UIView *)referenceView referenceFrame:(CGRect)referenceFrame parentView:(UIView *)parentView noTransitionView:(bool)noTransitionView
|
||||
{
|
||||
[super prepareTransitionInWithReferenceView:referenceView referenceFrame:referenceFrame parentView:parentView noTransitionView:noTransitionView];
|
||||
[self.view insertSubview:_transitionView belowSubview:_wrapperView];
|
||||
|
||||
if (self.initialAppearance && self.fromCamera)
|
||||
[self.view insertSubview:_transitionView belowSubview:_wrapperView];
|
||||
}
|
||||
|
||||
- (void)transitionIn
|
||||
@ -282,6 +269,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
{
|
||||
_portraitToolsWrapperView.alpha = 1.0f;
|
||||
_landscapeToolsWrapperView.alpha = 1.0f;
|
||||
_dotImageView.alpha = 1.0f;
|
||||
_dotMarkerView.alpha = 1.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
_scrubberView.layer.shouldRasterize = false;
|
||||
}];
|
||||
@ -387,6 +376,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
_cropView.transform = CGAffineTransformMakeScale(targetCropViewScale, targetCropViewScale);
|
||||
} completion:^(__unused BOOL finished)
|
||||
{
|
||||
[_cropView removeFromSuperview];
|
||||
_previewView.alpha = 1.0;
|
||||
if (self.finishedTransitionOut != nil)
|
||||
self.finishedTransitionOut();
|
||||
@ -394,8 +384,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
if (completion != nil)
|
||||
completion();
|
||||
}];
|
||||
} else if (self.fromCamera) {
|
||||
_previewView.alpha = 0.0f;
|
||||
} else {
|
||||
if (self.fromCamera)
|
||||
_previewView.alpha = 0.0f;
|
||||
}
|
||||
|
||||
switch (self.effectiveOrientation)
|
||||
@ -438,9 +429,11 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
_portraitToolsWrapperView.alpha = 0.0f;
|
||||
_landscapeToolsWrapperView.alpha = 0.0f;
|
||||
_dotImageView.alpha = 0.0f;
|
||||
_dotMarkerView.alpha = 0.0f;
|
||||
} completion:^(__unused BOOL finished)
|
||||
{
|
||||
if (!switching) {
|
||||
[_cropView removeFromSuperview];
|
||||
if (completion != nil)
|
||||
completion();
|
||||
}
|
||||
@ -544,17 +537,21 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
{
|
||||
_portraitToolsWrapperView.alpha = 0.0f;
|
||||
_landscapeToolsWrapperView.alpha = 0.0f;
|
||||
_videoAreaView.alpha = 0.0f;
|
||||
_dotImageView.alpha = 0.0f;
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)finishCustomTransitionOut
|
||||
{
|
||||
[_cropView removeFromSuperview];
|
||||
}
|
||||
|
||||
- (CGRect)transitionOutReferenceFrame
|
||||
{
|
||||
if (_dismissingToCamera) {
|
||||
return _fullPreviewView.frame;
|
||||
return [_fullPreviewView.superview convertRect:_fullPreviewView.frame toView:self.view];
|
||||
} else {
|
||||
return _previewView.frame;
|
||||
return [_wrapperView convertRect:_cropView.frame toView:self.view];
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,12 +732,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
|
||||
CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size);
|
||||
previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
|
||||
|
||||
[UIView performWithoutAnimation:^
|
||||
{
|
||||
_videoAreaView.frame = _previewView.frame;
|
||||
_flashView.frame = _videoAreaView.bounds;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateLayout:(UIInterfaceOrientation)orientation
|
||||
@ -826,29 +817,24 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
- (void)beginScrubbing:(bool)flash
|
||||
{
|
||||
if (flash)
|
||||
if (flash) {
|
||||
_coverLabel.alpha = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)endScrubbing:(bool)flash completion:(bool (^)(void))completion
|
||||
{
|
||||
if (flash) {
|
||||
[UIView animateWithDuration:0.12 animations:^{
|
||||
_flashView.alpha = 1.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_flashView.alpha = 0.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
|
||||
if (completion()) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_coverLabel.alpha = 0.7f;
|
||||
}];
|
||||
|
||||
self.controlVideoPlayback(true);
|
||||
}
|
||||
});
|
||||
}];
|
||||
[_cropView flash:^{
|
||||
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
|
||||
if (completion()) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_coverLabel.alpha = 0.7f;
|
||||
}];
|
||||
|
||||
self.controlVideoPlayback(true);
|
||||
}
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
TGDispatchAfter(1.32, dispatch_get_main_queue(), ^{
|
||||
|
@ -59,6 +59,8 @@
|
||||
TGPhotoEditorTab _currentTab;
|
||||
TGPhotoEditorTabController *_currentTabController;
|
||||
|
||||
TGMediaEditingContext *_standaloneEditingContext;
|
||||
|
||||
UIView *_backgroundView;
|
||||
UIView *_containerView;
|
||||
UIView *_wrapperView;
|
||||
@ -140,6 +142,7 @@
|
||||
{
|
||||
_context = context;
|
||||
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
|
||||
_standaloneEditingContext = [[TGMediaEditingContext alloc] init];
|
||||
|
||||
self.automaticallyManageScrollViewInsets = false;
|
||||
self.autoManageStatusBarBackground = false;
|
||||
@ -561,6 +564,9 @@
|
||||
|
||||
if (strongSelf->_ignoreDefaultPreviewViewTransitionIn)
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
TGDispatchOnMainThread(^
|
||||
{
|
||||
if (strongSelf->_dismissed)
|
||||
@ -575,6 +581,9 @@
|
||||
{
|
||||
[photoEditor processAnimated:false completion:^
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
TGDispatchOnMainThread(^
|
||||
{
|
||||
if (strongSelf->_dismissed)
|
||||
@ -1090,7 +1099,13 @@
|
||||
rep = imageView;
|
||||
}
|
||||
[_currentTabController prepareForCustomTransitionOut];
|
||||
self.beginCustomTransitionOut([_currentTabController transitionOutReferenceFrame], rep, completion);
|
||||
|
||||
TGPhotoEditorTabController *tabController = _currentTabController;
|
||||
self.beginCustomTransitionOut([_currentTabController transitionOutReferenceFrame], rep, ^{
|
||||
[tabController finishCustomTransitionOut];
|
||||
if (completion)
|
||||
completion();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1125,6 +1140,8 @@
|
||||
{
|
||||
if (![currentController isDismissAllowed])
|
||||
return;
|
||||
|
||||
[self savePaintingData];
|
||||
|
||||
currentController.switchingToTab = tab;
|
||||
[currentController transitionOutSwitching:true completion:^
|
||||
@ -1203,7 +1220,11 @@
|
||||
{
|
||||
bool skipInitialTransition = (![self presentedFromCamera] && self.navigationController != nil) || self.skipInitialTransition;
|
||||
|
||||
TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView dotImageView:_dotImageView fullPreviewView:_fullPreviewView];
|
||||
TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView];
|
||||
cropController.scrubberView = _scrubberView;
|
||||
cropController.dotImageView = _dotImageView;
|
||||
cropController.dotMarkerView = _dotMarkerView;
|
||||
cropController.fullPreviewView = _fullPreviewView;
|
||||
cropController.fromCamera = [self presentedFromCamera];
|
||||
cropController.skipTransitionIn = skipInitialTransition;
|
||||
if (snapshotImage != nil)
|
||||
@ -1636,6 +1657,7 @@
|
||||
self.view.frame = targetFrame;
|
||||
} completion:^(__unused BOOL finished)
|
||||
{
|
||||
[_currentTabController finishCustomTransitionOut];
|
||||
if (self.navigationController != nil) {
|
||||
[self.navigationController popViewControllerAnimated:false];
|
||||
} else {
|
||||
@ -1646,6 +1668,7 @@
|
||||
else
|
||||
{
|
||||
if (self.navigationController != nil) {
|
||||
[_currentTabController finishCustomTransitionOut];
|
||||
[self.navigationController popViewControllerAnimated:false];
|
||||
} else {
|
||||
[self dismiss];
|
||||
@ -1760,6 +1783,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)savePaintingData {
|
||||
if (![_currentTabController isKindOfClass:[TGPhotoPaintController class]])
|
||||
return;
|
||||
|
||||
TGPhotoPaintController *paintController = (TGPhotoPaintController *)_currentTabController;
|
||||
TGPaintingData *paintingData = [paintController paintingData];
|
||||
_photoEditor.paintingData = paintingData;
|
||||
|
||||
if (paintingData != nil)
|
||||
[TGPaintingData storePaintingData:paintingData inContext:self.editingContext forItem:_item forVideo:(_intent == TGPhotoEditorControllerVideoIntent)];
|
||||
}
|
||||
|
||||
- (void)applyEditor
|
||||
{
|
||||
if (![_currentTabController isDismissAllowed])
|
||||
@ -1768,8 +1803,6 @@
|
||||
self.view.userInteractionEnabled = false;
|
||||
[_currentTabController prepareTransitionOutSaving:true];
|
||||
|
||||
TGPaintingData *paintingData = _photoEditor.paintingData;
|
||||
|
||||
bool saving = true;
|
||||
NSTimeInterval videoStartValue = 0.0;
|
||||
NSTimeInterval trimStartValue = 0.0;
|
||||
@ -1777,12 +1810,7 @@
|
||||
|
||||
if ([_currentTabController isKindOfClass:[TGPhotoPaintController class]])
|
||||
{
|
||||
TGPhotoPaintController *paintController = (TGPhotoPaintController *)_currentTabController;
|
||||
paintingData = [paintController paintingData];
|
||||
_photoEditor.paintingData = paintingData;
|
||||
|
||||
if (paintingData != nil)
|
||||
[TGPaintingData storePaintingData:paintingData inContext:_editingContext forItem:_item forVideo:(_intent == TGPhotoEditorControllerVideoIntent)];
|
||||
[self savePaintingData];
|
||||
}
|
||||
else if ([_currentTabController isKindOfClass:[TGPhotoQualityController class]])
|
||||
{
|
||||
@ -1800,6 +1828,7 @@
|
||||
|
||||
[self stopVideoPlayback:true];
|
||||
|
||||
TGPaintingData *paintingData = _photoEditor.paintingData;
|
||||
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
|
||||
if ([self presentedForAvatarCreation] && _item.isVideo) {
|
||||
[[SQueue concurrentDefaultQueue] dispatch:^
|
||||
@ -1968,7 +1997,7 @@
|
||||
thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
[_editingContext setImage:fullImage thumbnailImage:thumbnailImage forItem:_item synchronous:true];
|
||||
[self.editingContext setImage:fullImage thumbnailImage:thumbnailImage forItem:_item synchronous:true];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
@ -1990,6 +2019,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (TGMediaEditingContext *)editingContext
|
||||
{
|
||||
if (_editingContext)
|
||||
return _editingContext;
|
||||
else
|
||||
return _standaloneEditingContext;
|
||||
}
|
||||
|
||||
- (void)doneButtonLongPressed:(UIButton *)sender
|
||||
{
|
||||
if (_intent == TGPhotoEditorControllerVideoIntent)
|
||||
@ -2591,7 +2628,7 @@
|
||||
return !strongSelf->_scrubberView.isScrubbing;
|
||||
}];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
TGDispatchAfter(0.16, dispatch_get_main_queue(), ^{
|
||||
[self updateDotImage:true];
|
||||
});
|
||||
}
|
||||
|
@ -215,6 +215,11 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
||||
|
||||
}
|
||||
|
||||
- (void)finishCustomTransitionOut
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))__unused completion
|
||||
{
|
||||
|
||||
|
@ -1842,7 +1842,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
_appeared = true;
|
||||
|
||||
if ([transitionView isKindOfClass:[TGPhotoEditorPreviewView class]]) {
|
||||
[_containerView insertSubview:transitionView belowSubview:_paintingWrapperView];
|
||||
|
||||
} else {
|
||||
[transitionView removeFromSuperview];
|
||||
}
|
||||
@ -1853,6 +1853,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
[previewView setPaintingHidden:true];
|
||||
previewView.hidden = false;
|
||||
[_containerView insertSubview:previewView belowSubview:_paintingWrapperView];
|
||||
[self updateContentViewLayout];
|
||||
[previewView performTransitionInIfNeeded];
|
||||
|
||||
CGRect rect = [self fittedCropRect:true];
|
||||
@ -2142,6 +2143,14 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
[self updateLayout:toInterfaceOrientation];
|
||||
}
|
||||
|
||||
- (void)updateContentViewLayout
|
||||
{
|
||||
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(_photoEditor.cropOrientation));
|
||||
_contentView.transform = rotationTransform;
|
||||
_contentView.frame = self.previewView.frame;
|
||||
[self resetScrollView];
|
||||
}
|
||||
|
||||
- (void)updateLayout:(UIInterfaceOrientation)orientation
|
||||
{
|
||||
if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
|
||||
|
@ -160,8 +160,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf->_hudView setText:nil];
|
||||
|
||||
if (forVideo) {
|
||||
strongSelf->_photoEditor.disableAll = false;
|
||||
strongSelf->_photoEditor.disableAll = false;
|
||||
if (!forVideo) {
|
||||
[strongSelf->_photoEditor processAnimated:false completion:nil];
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -171,8 +172,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf->_hudView setText:TGLocalized(@"PhotoEditor.Original")];
|
||||
|
||||
if (forVideo) {
|
||||
strongSelf->_photoEditor.disableAll = true;
|
||||
strongSelf->_photoEditor.disableAll = true;
|
||||
if (!forVideo) {
|
||||
[strongSelf->_photoEditor processAnimated:false completion:nil];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,59 @@
|
||||
|
||||
@implementation TGPhotoVideoEditor
|
||||
|
||||
+ (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo
|
||||
{
|
||||
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
|
||||
|
||||
id<TGMediaEditableItem> editableItem;
|
||||
if (image != nil) {
|
||||
editableItem = image;
|
||||
} else if (video != nil) {
|
||||
editableItem = [[TGCameraCapturedVideo alloc] initWithURL:video];
|
||||
}
|
||||
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:TGPhotoEditorControllerAvatarIntent adjustments:nil caption:nil screenImage:nil availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
// controller.stickersContext = _stickersContext;
|
||||
controller.dontHideStatusBar = true;
|
||||
controller.didFinishEditing = ^(__unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges)
|
||||
{
|
||||
if (didFinishWithImage != nil)
|
||||
didFinishWithImage(resultImage);
|
||||
};
|
||||
controller.didFinishEditingVideo = ^(NSURL *url, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges) {
|
||||
if (didFinishWithVideo != nil)
|
||||
didFinishWithVideo(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:controller contentController:controller];
|
||||
controllerWindow.hidden = false;
|
||||
controller.view.clipsToBounds = true;
|
||||
}
|
||||
|
||||
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed
|
||||
{
|
||||
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
|
||||
|
@ -6,6 +6,30 @@ import LegacyComponents
|
||||
import TelegramPresentationData
|
||||
import LegacyUI
|
||||
|
||||
public func presentLegacyAvatarEditor(theme: PresentationTheme, image: UIImage?, video: URL?, present: (ViewController, Any?) -> Void, imageCompletion: @escaping (UIImage) -> Void, videoCompletion: @escaping (UIImage, URL, TGVideoEditAdjustments?) -> Void) {
|
||||
let legacyController = LegacyController(presentation: .custom, theme: theme)
|
||||
legacyController.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
||||
let navigationController = makeLegacyNavigationController(rootController: emptyController)
|
||||
navigationController.setNavigationBarHidden(true, animated: false)
|
||||
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
|
||||
|
||||
legacyController.bind(controller: navigationController)
|
||||
|
||||
present(legacyController, nil)
|
||||
|
||||
TGPhotoVideoEditor.present(with: legacyController.context, parentController: emptyController, image: image, video: video, didFinishWithImage: { image in
|
||||
if let image = image {
|
||||
imageCompletion(image)
|
||||
}
|
||||
}, didFinishWithVideo: { image, url, adjustments in
|
||||
if let image = image, let url = url {
|
||||
videoCompletion(image, url, adjustments)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) {
|
||||
let legacyController = LegacyController(presentation: .custom, theme: theme)
|
||||
legacyController.statusBar.statusBarStyle = .Ignore
|
||||
|
@ -161,7 +161,7 @@ public final class MediaTrackFrameBuffer {
|
||||
if self.endOfStream, let decodedFrame = self.decoder.takeRemainingFrame() {
|
||||
return .frame(decodedFrame)
|
||||
} else {
|
||||
if let bufferedUntilTime = bufferedUntilTime {
|
||||
if let bufferedUntilTime = self.bufferedUntilTime {
|
||||
if CMTimeCompare(bufferedUntilTime, self.duration) >= 0 || self.endOfStream {
|
||||
return .finished
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ static_library(
|
||||
"//submodules/RadialStatusNode:RadialStatusNode",
|
||||
"//submodules/ShareController:ShareController",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
||||
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -22,6 +22,8 @@ swift_library(
|
||||
"//submodules/RadialStatusNode:RadialStatusNode",
|
||||
"//submodules/ShareController:ShareController",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
||||
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -10,6 +10,8 @@ import SyncCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import GalleryUI
|
||||
import LegacyMediaPickerUI
|
||||
import SaveToCameraRoll
|
||||
|
||||
public enum AvatarGalleryEntryId: Hashable {
|
||||
case topImage
|
||||
@ -192,6 +194,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
|
||||
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
|
||||
|
||||
private let editDisposable = MetaDisposable ()
|
||||
|
||||
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
@ -265,7 +269,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
})
|
||||
}, edit: { [weak self] in
|
||||
self?.editEntry(entry)
|
||||
})
|
||||
}), centralItemIndex: strongSelf.centralEntryIndex, synchronous: !isFirstTime)
|
||||
|
||||
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
|
||||
@ -332,6 +338,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
self.centralItemAttributesDisposable.dispose()
|
||||
self.editDisposable.dispose()
|
||||
}
|
||||
|
||||
@objc func donePressed() {
|
||||
@ -384,6 +391,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.pager.updateOnReplacement = true
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
self.galleryNode.navigationBar = self.navigationBar
|
||||
|
||||
@ -426,6 +434,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
}, edit: { [weak self] in
|
||||
self?.editEntry(entry)
|
||||
}) }), centralItemIndex: self.centralEntryIndex)
|
||||
|
||||
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
|
||||
@ -584,7 +594,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
}) }), centralItemIndex: 0)
|
||||
}, edit: { [weak self] in
|
||||
self?.editEntry(entry)
|
||||
}) }), centralItemIndex: 0, synchronous: true)
|
||||
self.entries = entries
|
||||
}
|
||||
} else {
|
||||
@ -605,6 +617,50 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
}
|
||||
|
||||
private func editEntry(_ rawEntry: AvatarGalleryEntry) {
|
||||
let mediaReference: AnyMediaReference
|
||||
if let video = rawEntry.videoRepresentations.last {
|
||||
mediaReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
|
||||
} else {
|
||||
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: rawEntry.representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
||||
mediaReference = .standalone(media: media)
|
||||
}
|
||||
|
||||
self.editDisposable.set((fetchMediaData(context: self.context, postbox: self.context.account.postbox, mediaReference: mediaReference)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state, isImage in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch state {
|
||||
case let .progress(value):
|
||||
break
|
||||
case let .data(data):
|
||||
let image: UIImage?
|
||||
let video: URL?
|
||||
if isImage {
|
||||
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
image = UIImage(data: fileData)
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
video = nil
|
||||
} else {
|
||||
image = nil
|
||||
video = URL(fileURLWithPath: data.path)
|
||||
}
|
||||
presentLegacyAvatarEditor(theme: strongSelf.presentationData.theme, image: image, video: video, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}
|
||||
}, imageCompletion: { [weak self] image in
|
||||
|
||||
}, videoCompletion: { [weak self] image, url, adjustments in
|
||||
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func deleteEntry(_ rawEntry: AvatarGalleryEntry) {
|
||||
var entry = rawEntry
|
||||
if case .topImage = entry, !self.entries.isEmpty {
|
||||
|
@ -54,8 +54,9 @@ class PeerAvatarImageGalleryItem: GalleryItem {
|
||||
let sourceHasRoundCorners: Bool
|
||||
let delete: (() -> Void)?
|
||||
let setMain: (() -> Void)?
|
||||
let edit: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, peer: Peer, presentationData: PresentationData, entry: AvatarGalleryEntry, sourceHasRoundCorners: Bool, delete: (() -> Void)?, setMain: (() -> Void)?) {
|
||||
init(context: AccountContext, peer: Peer, presentationData: PresentationData, entry: AvatarGalleryEntry, sourceHasRoundCorners: Bool, delete: (() -> Void)?, setMain: (() -> Void)?, edit: (() -> Void)?) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.presentationData = presentationData
|
||||
@ -63,6 +64,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
|
||||
self.sourceHasRoundCorners = sourceHasRoundCorners
|
||||
self.delete = delete
|
||||
self.setMain = setMain
|
||||
self.edit = edit
|
||||
}
|
||||
|
||||
func node(synchronous: Bool) -> GalleryItemNode {
|
||||
@ -75,6 +77,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
|
||||
node.setEntry(self.entry, synchronous: synchronous)
|
||||
node.footerContentNode.delete = self.delete
|
||||
node.footerContentNode.setMain = self.setMain
|
||||
node.edit = self.edit
|
||||
|
||||
return node
|
||||
}
|
||||
@ -84,10 +87,17 @@ class PeerAvatarImageGalleryItem: GalleryItem {
|
||||
if let indexData = self.entry.indexData {
|
||||
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
|
||||
}
|
||||
|
||||
let previousContentAnimations = node.imageNode.contentAnimations
|
||||
if synchronous {
|
||||
node.imageNode.contentAnimations = []
|
||||
}
|
||||
node.setEntry(self.entry, synchronous: synchronous)
|
||||
if synchronous {
|
||||
node.imageNode.contentAnimations = previousContentAnimations
|
||||
}
|
||||
node.footerContentNode.delete = self.delete
|
||||
node.footerContentNode.setMain = self.setMain
|
||||
node.edit = self.edit
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +135,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var entry: AvatarGalleryEntry?
|
||||
|
||||
private let contentNode: PeerAvatarImageGalleryContentNode
|
||||
private let imageNode: TransformImageNode
|
||||
fileprivate let imageNode: TransformImageNode
|
||||
private var videoNode: UniversalVideoNode?
|
||||
private var videoContent: NativeVideoContent?
|
||||
|
||||
@ -142,6 +152,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var status: MediaResourceStatus?
|
||||
private let playbackStatusDisposable = MetaDisposable()
|
||||
|
||||
fileprivate var edit: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, peer: Peer, sourceHasRoundCorners: Bool) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
@ -212,7 +224,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
var footerContent: AvatarGalleryItemFooterContent
|
||||
if self.peer.id == self.context.account.peerId {
|
||||
footerContent = .own((entry.indexData?.position ?? 0) == 0)
|
||||
let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Settings_EditProfileMedia, style: .plain, target: self, action: #selector(editPressed))
|
||||
let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Settings_EditProfileMedia, style: .plain, target: self, action: #selector(self.editPressed))
|
||||
barButtonItems.append(rightBarButtonItem)
|
||||
} else {
|
||||
footerContent = .info
|
||||
@ -507,7 +519,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
|
||||
@objc private func editPressed() {
|
||||
|
||||
self.edit?()
|
||||
}
|
||||
|
||||
override func footerContent() -> Signal<(GalleryFooterContentNode?, GalleryOverlayContentNode?), NoError> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user