mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Video avatar fixes
This commit is contained in:
parent
9d435f3f25
commit
4fadec2ff3
@ -5548,12 +5548,15 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"PhotoEditor.BlurToolPortrait" = "Portrait";
|
||||
"PhotoEditor.SelectCoverFrame" = "Choose a cover for your profile video";
|
||||
|
||||
"Conversation.PeerNearbyTitle" = "%@ is %@ away";
|
||||
"Conversation.PeerNearbyTitle" = "%1$@ is %2$@ away";
|
||||
"Conversation.PeerNearbyText" = "Send a message or tap on the greeting below to show that you are ready to chat.";
|
||||
"Conversation.PeerNearbyDistance" = "%@ is %@ away";
|
||||
"Conversation.PeerNearbyDistance" = "%1$@ is %2$@ away";
|
||||
|
||||
"ProfilePhoto.MainPhoto" = "Main Photo";
|
||||
"ProfilePhoto.SetMain" = "Set as Main Photo";
|
||||
"ProfilePhoto.SetMainPhoto" = "Set as Main Photo";
|
||||
|
||||
"ProfilePhoto.MainVideo" = "Main Photo";
|
||||
"ProfilePhoto.SetMainVideo" = "Set as Main Photo";
|
||||
|
||||
"Stats.GroupOverview" = "OVERVIEW";
|
||||
"Stats.GroupMembers" = "Members";
|
||||
|
@ -204,13 +204,20 @@ public final class ChatPeekTimeout {
|
||||
|
||||
public final class ChatPeerNearbyData: Equatable {
|
||||
public static func == (lhs: ChatPeerNearbyData, rhs: ChatPeerNearbyData) -> Bool {
|
||||
if let lhsSticker = lhs.sticker, let rhsSticker = rhs.sticker, !lhsSticker.isEqual(to: rhsSticker) {
|
||||
return false
|
||||
} else if (lhs.sticker == nil) != (rhs.sticker == nil) {
|
||||
return false
|
||||
}
|
||||
return lhs.distance == rhs.distance
|
||||
}
|
||||
|
||||
public let distance: Int32
|
||||
public let sticker: TelegramMediaFile?
|
||||
|
||||
public init(distance: Int32) {
|
||||
public init(distance: Int32, sticker: TelegramMediaFile?) {
|
||||
self.distance = distance
|
||||
self.sticker = sticker
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,8 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
|
||||
|
||||
self.scrollNode.view.delegate = self
|
||||
self.scrollNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
}
|
||||
@ -88,7 +90,7 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
|
||||
|
||||
public func updateItems(_ items: [GalleryThumbnailItem], indexes: [Int], centralIndex: Int, progress: CGFloat) {
|
||||
self.indexes = indexes
|
||||
var items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
|
||||
let items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
|
||||
var updated = false
|
||||
if self.items.count == items.count {
|
||||
for i in 0 ..< self.items.count {
|
||||
|
@ -58,8 +58,6 @@ typedef enum {
|
||||
@property (nonatomic, strong) PGCameraShotMetadata *metadata;
|
||||
@property (nonatomic, strong) NSArray *faces;
|
||||
|
||||
@property (nonatomic, strong) NSArray *cachedVideoThumbnails;
|
||||
|
||||
@property (nonatomic, strong) AVPlayer *player;
|
||||
|
||||
@property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView;
|
||||
|
@ -16,6 +16,7 @@
|
||||
- (void)prepareToRecord;
|
||||
|
||||
- (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime;
|
||||
- (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer;
|
||||
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer;
|
||||
|
||||
- (void)finishRecording:(void(^)())completed;
|
||||
|
@ -22,6 +22,7 @@
|
||||
@property (nonatomic, assign) TGMediaVideoConversionPreset preset;
|
||||
|
||||
@property (nonatomic, weak) TGPhotoEditorPreviewView *previewOutput;
|
||||
@property (nonatomic, strong) NSArray *additionalOutputs;
|
||||
@property (nonatomic, readonly) NSArray *tools;
|
||||
|
||||
@property (nonatomic, readonly) bool processing;
|
||||
|
@ -414,12 +414,34 @@
|
||||
[_finalFilter addTarget:previewOutput.imageView];
|
||||
}
|
||||
|
||||
for (PGPhotoEditorView *view in _additionalOutputs) {
|
||||
[_finalFilter addTarget:view];
|
||||
}
|
||||
|
||||
if (_histogramGenerator != nil && !self.standalone) {
|
||||
[_finalFilter addTarget:_histogramGenerator];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAdditionalOutputs:(NSArray *)additionalOutputs {
|
||||
_additionalOutputs = additionalOutputs;
|
||||
|
||||
[_finalFilter removeAllTargets];
|
||||
|
||||
if (self.previewOutput != nil) {
|
||||
[_finalFilter addTarget:self.previewOutput.imageView];
|
||||
}
|
||||
|
||||
for (PGPhotoEditorView *view in _additionalOutputs) {
|
||||
[_finalFilter addTarget:view];
|
||||
}
|
||||
|
||||
if (_histogramGenerator != nil && !self.standalone) {
|
||||
[_finalFilter addTarget:_histogramGenerator];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Result
|
||||
|
||||
- (void)createResultImageWithCompletion:(void (^)(UIImage *image))completion
|
||||
|
@ -100,7 +100,6 @@
|
||||
glEnableVertexAttribArray(displayTextureCoordinateAttribute);
|
||||
|
||||
[self setBackgroundColorRed:0.0 green:0.0 blue:0.0 alpha:1.0];
|
||||
// _fillMode = kGPUImageFillModePreserveAspectRatio;
|
||||
[self createDisplayFramebuffer];
|
||||
});
|
||||
}
|
||||
|
@ -912,7 +912,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
||||
{
|
||||
case PGCameraModePhoto:
|
||||
{
|
||||
if (_intent == TGCameraControllerGenericIntent)
|
||||
if (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent)
|
||||
{
|
||||
_switchToVideoTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
|
||||
}
|
||||
@ -2288,7 +2288,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
||||
if (gestureRecognizer == _panGestureRecognizer)
|
||||
return !_camera.isRecordingVideo && _items.count == 0;
|
||||
else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer)
|
||||
return _intent == TGCameraControllerGenericIntent && !_camera.isRecordingVideo;
|
||||
return (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent) && !_camera.isRecordingVideo;
|
||||
else if (gestureRecognizer == _pinchGestureRecognizer)
|
||||
return _camera.isZoomAvailable;
|
||||
|
||||
|
@ -40,12 +40,14 @@ typedef enum
|
||||
UIControl *_scrubberHandle;
|
||||
|
||||
UIControl *_dotHandle;
|
||||
UIView *_dotContentView;
|
||||
UIImageView *_dotImageView;
|
||||
__weak UIView *_dotVideoView;
|
||||
UIImageView *_dotFrameView;
|
||||
|
||||
UIPanGestureRecognizer *_panGestureRecognizer;
|
||||
UILongPressGestureRecognizer *_pressGestureRecognizer;
|
||||
UITapGestureRecognizer *_tapGestureRecognizer;
|
||||
|
||||
bool _beganInteraction;
|
||||
bool _endedInteraction;
|
||||
@ -235,10 +237,7 @@ typedef enum
|
||||
CGFloat delta = originX - trimView.frame.origin.x;
|
||||
CGFloat maxWidth = availableTrimRect.size.width + normalScrubbingRect.origin.x * 2 - originX;
|
||||
|
||||
CGRect trimViewRect = CGRectMake(originX,
|
||||
trimView.frame.origin.y,
|
||||
MIN(maxWidth, trimView.frame.size.width - delta),
|
||||
trimView.frame.size.height);
|
||||
CGRect trimViewRect = CGRectMake(originX, trimView.frame.origin.y, MIN(maxWidth, trimView.frame.size.width - delta), trimView.frame.size.height);
|
||||
|
||||
NSTimeInterval trimStartPosition = 0.0;
|
||||
NSTimeInterval trimEndPosition = 0.0;
|
||||
@ -251,10 +250,7 @@ typedef enum
|
||||
|
||||
if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength)
|
||||
{
|
||||
trimViewRect = CGRectMake(trimView.frame.origin.x + delta,
|
||||
trimView.frame.origin.y,
|
||||
trimView.frame.size.width,
|
||||
trimView.frame.size.height);
|
||||
trimViewRect = CGRectMake(trimView.frame.origin.x + delta, trimView.frame.origin.y, trimView.frame.size.width, trimView.frame.size.height);
|
||||
|
||||
[strongSelf _trimStartPosition:&trimStartPosition trimEndPosition:&trimEndPosition forTrimFrame:trimViewRect duration:strongSelf.duration];
|
||||
}
|
||||
@ -276,6 +272,9 @@ typedef enum
|
||||
UIView *handle = strongSelf->_scrubberHandle;
|
||||
handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y);
|
||||
|
||||
UIView *dotHandle = strongSelf->_dotHandle;
|
||||
dotHandle.center = CGPointMake(trimView.frame.origin.x + 12 + dotHandle.frame.size.width / 2, dotHandle.center.y);
|
||||
|
||||
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate;
|
||||
if ([delegate respondsToSelector:@selector(videoScrubber:editingStartValueDidChange:)])
|
||||
[delegate videoScrubber:strongSelf editingStartValueDidChange:trimStartPosition];
|
||||
@ -345,6 +344,9 @@ typedef enum
|
||||
UIView *handle = strongSelf->_scrubberHandle;
|
||||
handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y);
|
||||
|
||||
UIView *dotHandle = strongSelf->_dotHandle;
|
||||
dotHandle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 + dotHandle.frame.size.width / 2, dotHandle.center.y);
|
||||
|
||||
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate;
|
||||
if ([delegate respondsToSelector:@selector(videoScrubber:editingEndValueDidChange:)])
|
||||
[delegate videoScrubber:strongSelf editingEndValueDidChange:trimEndPosition];
|
||||
@ -380,10 +382,14 @@ typedef enum
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
_dotImageView = [[UIImageView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)];
|
||||
_dotContentView = [[UIView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)];
|
||||
_dotContentView.clipsToBounds = true;
|
||||
[_dotHandle addSubview:_dotContentView];
|
||||
|
||||
_dotImageView = [[UIImageView alloc] initWithFrame:_dotContentView.bounds];
|
||||
_dotImageView.clipsToBounds = true;
|
||||
_dotImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[_dotHandle addSubview:_dotImageView];
|
||||
[_dotContentView addSubview:_dotImageView];
|
||||
|
||||
_dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds];
|
||||
_dotFrameView.image = dotFrameImage;
|
||||
@ -423,6 +429,10 @@ typedef enum
|
||||
[_scrubberHandle addGestureRecognizer:_panGestureRecognizer];
|
||||
[_dotHandle addGestureRecognizer:_panGestureRecognizer];
|
||||
|
||||
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
|
||||
_tapGestureRecognizer.enabled = false;
|
||||
[_trimView addGestureRecognizer:_tapGestureRecognizer];
|
||||
|
||||
_arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")];
|
||||
_arrowView.alpha = 0.45f;
|
||||
_arrowView.hidden = true;
|
||||
@ -453,12 +463,13 @@ typedef enum
|
||||
_hasDotPicker = hasDotPicker;
|
||||
_dotHandle.hidden = !hasDotPicker;
|
||||
_scrubberHandle.hidden = true;
|
||||
_tapGestureRecognizer.enabled = hasDotPicker;
|
||||
}
|
||||
|
||||
- (void)setDotVideoView:(UIView *)dotVideoView {
|
||||
_dotVideoView = dotVideoView;
|
||||
_dotVideoView.frame = CGRectInset(_dotHandle.bounds, 2.0, 2.0);
|
||||
[_dotHandle insertSubview:dotVideoView belowSubview:_dotFrameView];
|
||||
_dotVideoView.frame = _dotImageView.bounds;
|
||||
[_dotContentView addSubview:_dotVideoView];
|
||||
}
|
||||
|
||||
- (void)setDotImage:(UIImage *)dotImage {
|
||||
@ -674,9 +685,14 @@ typedef enum
|
||||
frameAspectRatio = _cropRect.size.width / _cropRect.size.height;
|
||||
else
|
||||
frameAspectRatio = originalAspectRatio;
|
||||
|
||||
|
||||
_thumbnailAspectRatio = frameAspectRatio;
|
||||
|
||||
if (_hasDotPicker) {
|
||||
CGSize videoSize = TGFillSize([self _thumbnailSize], _dotImageView.frame.size);
|
||||
_dotImageView.frame = CGRectMake(TGScreenPixelFloor((_dotImageView.frame.size.width - videoSize.width) / 2.0), 0.0, videoSize.width, videoSize.height);
|
||||
}
|
||||
|
||||
NSInteger thumbnailCount = (NSInteger)CGCeil(_summaryThumbnailWrapperView.frame.size.width / [self _thumbnailSizeWithAspectRatio:frameAspectRatio orientation:_cropOrientation].width);
|
||||
|
||||
if ([dataSource respondsToSelector:@selector(videoScrubber:evenlySpacedTimestamps:startingAt:endingAt:)])
|
||||
@ -1200,6 +1216,30 @@ typedef enum
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
CGPoint location = [gestureRecognizer locationInView:_dotHandle.superview];
|
||||
|
||||
CGFloat position = MAX(_trimStartValue, MIN(_trimEndValue, [self _positionForScrubberPosition:location duration:_duration]));
|
||||
_value = position;
|
||||
|
||||
CGPoint center = [self _dotPositionForPosition:position duration:_duration];
|
||||
[UIView animateWithDuration:0.2 delay:0.0 usingSpringWithDamping:1.1 initialSpringVelocity:0.0 options:kNilOptions animations:^{
|
||||
_dotHandle.center = CGPointMake(center.x, _dotHandle.center.y);
|
||||
} completion:^(BOOL finished) {
|
||||
}];
|
||||
|
||||
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = self.delegate;
|
||||
if ([delegate respondsToSelector:@selector(videoScrubberDidBeginScrubbing:)])
|
||||
[delegate videoScrubberDidBeginScrubbing:self];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(videoScrubber:valueDidChange:)])
|
||||
[delegate videoScrubber:self valueDidChange:position];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(videoScrubberDidEndScrubbing:)])
|
||||
[delegate videoScrubberDidEndScrubbing:self];
|
||||
}
|
||||
|
||||
- (void)setScrubberHandleHidden:(bool)hidden animated:(bool)animated
|
||||
{
|
||||
if (animated)
|
||||
|
@ -4,4 +4,6 @@
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image originalSize:(CGSize)originalSize cropRect:(CGRect)cropRect cropOrientation:(UIImageOrientation)cropOrientation cropMirrored:(bool)cropMirrored;
|
||||
|
||||
- (void)updateCropping;
|
||||
|
||||
@end
|
||||
|
@ -56,6 +56,10 @@
|
||||
if (_imageView == nil)
|
||||
return;
|
||||
|
||||
[self updateCropping];
|
||||
}
|
||||
|
||||
- (void)updateCropping {
|
||||
CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(_cropOrientation));
|
||||
if (_cropMirrored)
|
||||
transform = CGAffineTransformScale(transform, -1.0f, 1.0f);
|
||||
@ -79,11 +83,11 @@
|
||||
cropRect = CGRectMake(originalSize.width - cropRect.size.width - cropRect.origin.x, originalSize.height - cropRect.size.height - cropRect.origin.y, cropRect.size.width, cropRect.size.height);
|
||||
}
|
||||
|
||||
CGFloat ratio = frame.size.width / cropRect.size.width;
|
||||
CGFloat ratio = self.frame.size.width / cropRect.size.width;
|
||||
_imageView.frame = CGRectMake(-cropRect.origin.x * ratio, -cropRect.origin.y * ratio, originalSize.width * ratio, originalSize.height * ratio);
|
||||
|
||||
CGFloat thickness = 1.0f - TGRetinaPixel;
|
||||
_stripeView.frame = CGRectMake(frame.size.width - thickness, 0, thickness, frame.size.height);
|
||||
_stripeView.frame = CGRectMake(self.frame.size.width - thickness, 0, thickness, self.frame.size.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -342,22 +342,18 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f;
|
||||
|
||||
UIInterfaceOrientation orientation = self.effectiveOrientation;
|
||||
|
||||
bool hasOnScreenNavigation = false;
|
||||
if (iosMajorVersion() >= 11)
|
||||
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
|
||||
|
||||
CGRect cropRectFrame = [_cropView cropRectFrameForView:self.view];
|
||||
CGSize referenceSize = [self referenceViewSizeForOrientation:orientation];
|
||||
CGRect referenceBounds = CGRectMake(0, 0, referenceSize.width, referenceSize.height);
|
||||
CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
|
||||
CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
|
||||
|
||||
if (self.switchingToTab == TGPhotoEditorPreviewTab)
|
||||
{
|
||||
containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation];
|
||||
containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
|
||||
}
|
||||
else if (self.switchingToTab == TGPhotoEditorPaintTab)
|
||||
{
|
||||
containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
|
||||
containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
|
||||
}
|
||||
|
||||
CGSize fittedSize = TGScaleToSize(cropRectFrame.size, containerFrame.size);
|
||||
|
@ -3,16 +3,14 @@
|
||||
@class PGPhotoEditor;
|
||||
@class PGPhotoTool;
|
||||
@class TGPhotoEditorPreviewView;
|
||||
@class TGMediaPickerGalleryVideoScrubber;
|
||||
|
||||
@interface TGPhotoAvatarPreviewController : TGPhotoEditorTabController
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView;
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView;
|
||||
|
||||
- (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset;
|
||||
- (void)setScrubberPlaying:(bool)value;
|
||||
|
||||
- (NSTimeInterval)coverPosition;
|
||||
- (NSTimeInterval)trimStartValue;
|
||||
- (NSTimeInterval)trimEndValue;
|
||||
- (void)beginScrubbing;
|
||||
- (void)endScrubbing:(bool (^)(void))completion;
|
||||
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated;
|
||||
|
||||
@end
|
||||
|
@ -6,6 +6,7 @@
|
||||
#import "TGPhotoEditorInterfaceAssets.h"
|
||||
|
||||
#import "PGPhotoEditor.h"
|
||||
#import "PGPhotoEditorView.h"
|
||||
#import <LegacyComponents/TGPhotoEditorUtils.h>
|
||||
#import <LegacyComponents/TGPaintUtils.h>
|
||||
|
||||
@ -19,11 +20,12 @@
|
||||
const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f;
|
||||
const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f;
|
||||
|
||||
@interface TGPhotoAvatarPreviewController () <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate>
|
||||
@interface TGPhotoAvatarPreviewController ()
|
||||
{
|
||||
bool _appeared;
|
||||
|
||||
TGPhotoEditorSparseView *_wrapperView;
|
||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||
UIView *_portraitToolsWrapperView;
|
||||
UIView *_landscapeToolsWrapperView;
|
||||
UIView *_portraitWrapperBackgroundView;
|
||||
@ -35,13 +37,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
UIView *_landscapeToolControlView;
|
||||
UIImageView *_areaMaskView;
|
||||
CGFloat _currentDiameter;
|
||||
|
||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||
TGModernGalleryVideoView *_dotVideoView;
|
||||
UILabel *_coverLabel;
|
||||
bool _wasPlayingBeforeScrubbing;
|
||||
bool _requestingThumbnails;
|
||||
SMetaDisposable *_thumbnailsDisposable;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) PGPhotoEditor *photoEditor;
|
||||
@ -51,24 +47,18 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
@implementation TGPhotoAvatarPreviewController
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView
|
||||
{
|
||||
self = [super initWithContext:context];
|
||||
if (self != nil)
|
||||
{
|
||||
self.photoEditor = photoEditor;
|
||||
self.previewView = previewView;
|
||||
|
||||
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
|
||||
_scrubberView = scrubberView;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_thumbnailsDisposable dispose];
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
@ -114,10 +104,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
_areaMaskView.alpha = 0.0f;
|
||||
[self.view insertSubview:_areaMaskView aboveSubview:_videoAreaView];
|
||||
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolsWrapperView.frame.size.width, 68.0f)];
|
||||
_scrubberView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
_scrubberView.dataSource = self;
|
||||
_scrubberView.delegate = self;
|
||||
[_portraitToolsWrapperView addSubview:_scrubberView];
|
||||
|
||||
_coverLabel = [[UILabel alloc] init];
|
||||
@ -137,22 +123,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
[self transitionIn];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
|
||||
_scrubberView.hasDotPicker = true;
|
||||
_scrubberView.disableZoom = true;
|
||||
_scrubberView.disableTimeDisplay = true;
|
||||
_scrubberView.trimStartValue = 0.0;
|
||||
_scrubberView.trimEndValue = MIN(9.9, self.item.originalDuration);
|
||||
[_scrubberView setTrimApplied:self.item.originalDuration > 9.9];
|
||||
_scrubberView.maximumLength = 9.9;
|
||||
[_scrubberView reloadData];
|
||||
[_scrubberView resetToStart];
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
TGPhotoEditorPreviewView *previewView = self.previewView;
|
||||
@ -264,6 +234,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
{
|
||||
_dismissing = true;
|
||||
|
||||
self.photoEditor.additionalOutputs = @[];
|
||||
|
||||
TGPhotoEditorPreviewView *previewView = self.previewView;
|
||||
[previewView prepareForTransitionOut];
|
||||
|
||||
@ -488,7 +460,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
_portraitToolsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize);
|
||||
|
||||
_scrubberView.frame = CGRectMake(0.0, 0.0, _portraitToolsWrapperView.frame.size.width, _scrubberView.frame.size.height);
|
||||
_coverLabel.frame = CGRectMake(floor((_portraitToolsWrapperView.frame.size.width - _coverLabel.frame.size.width) / 2.0), CGRectGetMaxY(_scrubberView.frame) + 6.0, _coverLabel.frame.size.width, _coverLabel.frame.size.height);
|
||||
// _portraitCollectionView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, panelSize);
|
||||
}
|
||||
@ -551,11 +522,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
if (!_dismissing)
|
||||
[self updateToolViews];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_scrubberView reloadThumbnails];
|
||||
});
|
||||
|
||||
|
||||
[self updatePreviewView];
|
||||
}
|
||||
|
||||
@ -571,42 +538,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
- (TGPhotoEditorTab)highlightedTabs
|
||||
{
|
||||
bool hasSimpleValue = false;
|
||||
bool hasBlur = false;
|
||||
bool hasCurves = false;
|
||||
bool hasTint = false;
|
||||
|
||||
// for (PGPhotoTool *tool in _allTools)
|
||||
// {
|
||||
// if (tool.isSimple)
|
||||
// {
|
||||
// if (tool.stringValue != nil)
|
||||
// hasSimpleValue = true;
|
||||
// }
|
||||
// else if ([tool isKindOfClass:[PGBlurTool class]] && tool.stringValue != nil)
|
||||
// {
|
||||
// hasBlur = true;
|
||||
// }
|
||||
// else if ([tool isKindOfClass:[PGCurvesTool class]] && tool.stringValue != nil)
|
||||
// {
|
||||
// hasCurves = true;
|
||||
// }
|
||||
// else if ([tool isKindOfClass:[PGTintTool class]] && tool.stringValue != nil)
|
||||
// {
|
||||
// hasTint = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
|
||||
TGPhotoEditorTab tabs = TGPhotoEditorNoneTab;
|
||||
|
||||
if (hasSimpleValue)
|
||||
if (adjustments.toolsApplied)
|
||||
tabs |= TGPhotoEditorToolsTab;
|
||||
if (hasBlur)
|
||||
tabs |= TGPhotoEditorBlurTab;
|
||||
if (hasCurves)
|
||||
tabs |= TGPhotoEditorCurvesTab;
|
||||
if (hasTint)
|
||||
tabs |= TGPhotoEditorTintTab;
|
||||
if (adjustments.hasPainting)
|
||||
tabs |= TGPhotoEditorPaintTab;
|
||||
|
||||
return tabs;
|
||||
}
|
||||
@ -632,30 +570,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
// }
|
||||
}
|
||||
|
||||
#pragma mark - Video Scrubber Data Source & Delegate
|
||||
|
||||
#pragma mark Scrubbing
|
||||
|
||||
- (NSTimeInterval)videoScrubberDuration:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
- (void)beginScrubbing
|
||||
{
|
||||
return self.item.originalDuration;
|
||||
}
|
||||
|
||||
- (CGFloat)videoScrubberThumbnailAspectRatio:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
if (CGSizeEqualToSize(self.item.originalSize, CGSizeZero))
|
||||
return 1.0f;
|
||||
|
||||
return self.item.originalSize.width / self.item.originalSize.height;
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidBeginScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
_wasPlayingBeforeScrubbing = true;
|
||||
self.controlVideoPlayback(false);
|
||||
|
||||
_dotVideoView.hidden = false;
|
||||
|
||||
_coverLabel.alpha = 1.0f;
|
||||
|
||||
[self setPlayButtonHidden:true animated:false];
|
||||
@ -665,21 +581,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
- (void)endScrubbing:(bool (^)(void))completion
|
||||
{
|
||||
AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player;
|
||||
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];
|
||||
generator.appliesPreferredTrackTransform = true;
|
||||
generator.maximumSize = CGSizeMake(128.0f, 128.0f);
|
||||
generator.requestedTimeToleranceAfter = kCMTimeZero;
|
||||
generator.requestedTimeToleranceBefore = kCMTimeZero;
|
||||
CGImageRef imageRef = [generator copyCGImageAtTime:player.currentItem.currentTime actualTime:NULL error:NULL];
|
||||
UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:imageRef];
|
||||
CGImageRelease(imageRef);
|
||||
|
||||
[_scrubberView setDotImage:thumbnailImage];
|
||||
_dotVideoView.hidden = true;
|
||||
|
||||
[UIView animateWithDuration:0.12 animations:^{
|
||||
_flashView.alpha = 1.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
@ -687,212 +590,17 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
_flashView.alpha = 0.0f;
|
||||
} completion:^(BOOL finished) {
|
||||
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
|
||||
if (_scrubberView.isScrubbing) {
|
||||
return;
|
||||
if (completion()) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_areaMaskView.alpha = 0.0f;
|
||||
_coverLabel.alpha = 0.7f;
|
||||
}];
|
||||
|
||||
self.controlVideoPlayback(true);
|
||||
}
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_areaMaskView.alpha = 0.0f;
|
||||
_coverLabel.alpha = 0.7f;
|
||||
}];
|
||||
|
||||
self.controlVideoPlayback(true);
|
||||
});
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
|
||||
{
|
||||
self.controlVideoSeek(position);
|
||||
}
|
||||
|
||||
#pragma mark Trimming
|
||||
|
||||
- (bool)hasTrimming
|
||||
{
|
||||
return _scrubberView.hasTrimming;
|
||||
}
|
||||
|
||||
- (CMTimeRange)trimRange
|
||||
{
|
||||
return CMTimeRangeMake(CMTimeMakeWithSeconds(_scrubberView.trimStartValue , NSEC_PER_SEC), CMTimeMakeWithSeconds((_scrubberView.trimEndValue - _scrubberView.trimStartValue), NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidBeginEditing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
self.controlVideoPlayback(false);
|
||||
|
||||
[self setPlayButtonHidden:true animated:false];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidEndEditing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
|
||||
{
|
||||
[self updatePlayerRange:videoScrubber.trimEndValue];
|
||||
|
||||
self.controlVideoSeek(videoScrubber.trimStartValue);
|
||||
self.controlVideoPlayback(true);
|
||||
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
|
||||
{
|
||||
self.controlVideoSeek(startValue);
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingEndValueDidChange:(NSTimeInterval)endValue
|
||||
{
|
||||
self.controlVideoSeek(endValue);
|
||||
}
|
||||
|
||||
- (void)updatePlayerRange:(NSTimeInterval)trimEndValue
|
||||
{
|
||||
self.controlVideoEndTime(trimEndValue);
|
||||
}
|
||||
|
||||
#pragma mark Thumbnails
|
||||
|
||||
- (NSArray *)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber evenlySpacedTimestamps:(NSInteger)count startingAt:(NSTimeInterval)startTimestamp endingAt:(NSTimeInterval)endTimestamp
|
||||
{
|
||||
if (endTimestamp < startTimestamp)
|
||||
return nil;
|
||||
|
||||
if (count == 0)
|
||||
return nil;
|
||||
|
||||
NSTimeInterval duration = [self videoScrubberDuration:videoScrubber];
|
||||
if (endTimestamp > duration)
|
||||
endTimestamp = duration;
|
||||
|
||||
NSTimeInterval interval = (endTimestamp - startTimestamp) / count;
|
||||
|
||||
NSMutableArray *timestamps = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = 0; i < count; i++)
|
||||
[timestamps addObject:@(startTimestamp + i * interval)];
|
||||
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
|
||||
{
|
||||
if (timestamps.count == 0)
|
||||
return;
|
||||
|
||||
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
|
||||
|
||||
NSArray *cachedThumbnails = ((TGPhotoEditorController *)self.parentViewController).cachedVideoThumbnails;
|
||||
|
||||
SSignal *thumbnailsSignal = nil;
|
||||
if (cachedThumbnails.count > 0) {
|
||||
thumbnailsSignal = [SSignal single:cachedThumbnails];
|
||||
} else if ([self.item isKindOfClass:[TGMediaAsset class]]) {
|
||||
thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps];
|
||||
} else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||
thumbnailsSignal = [((TGCameraCapturedVideo *)self.item).avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
|
||||
return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps];
|
||||
}];
|
||||
}
|
||||
|
||||
_requestingThumbnails = true;
|
||||
|
||||
__weak TGPhotoAvatarPreviewController *weakSelf = self;
|
||||
[_thumbnailsDisposable setDisposable:[[[[thumbnailsSignal onNext:^(NSArray *images) {
|
||||
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
TGDispatchOnMainThread(^{
|
||||
((TGPhotoEditorController *)strongSelf.parentViewController).cachedVideoThumbnails = images;
|
||||
});
|
||||
}] map:^NSArray *(NSArray *images) {
|
||||
if (adjustments.toolsApplied) {
|
||||
NSMutableArray *editedImages = [[NSMutableArray alloc] init];
|
||||
PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true];
|
||||
editor.standalone = true;
|
||||
for (UIImage *image in images) {
|
||||
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
|
||||
UIImage *resultImage = editor.currentResultImage;
|
||||
if (resultImage != nil) {
|
||||
[editedImages addObject:resultImage];
|
||||
} else {
|
||||
[editedImages addObject:image];
|
||||
}
|
||||
}
|
||||
return editedImages;
|
||||
} else {
|
||||
return images;
|
||||
}
|
||||
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *images)
|
||||
{
|
||||
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
|
||||
{
|
||||
if (index < timestamps.count)
|
||||
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] isSummaryThubmnail:isSummaryThumbnails];
|
||||
}];
|
||||
} completed:^
|
||||
{
|
||||
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
strongSelf->_requestingThumbnails = false;
|
||||
}]];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidFinishRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
_requestingThumbnails = false;
|
||||
|
||||
// [self setScrubbingPanelHidden:false animated:true];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidCancelRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
_requestingThumbnails = false;
|
||||
}
|
||||
|
||||
- (CGSize)videoScrubberOriginalSize:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber cropRect:(CGRect *)cropRect cropOrientation:(UIImageOrientation *)cropOrientation cropMirrored:(bool *)cropMirrored
|
||||
{
|
||||
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
|
||||
if (cropRect != NULL)
|
||||
*cropRect = (adjustments != nil) ? adjustments.cropRect : CGRectMake(0, 0, self.item.originalSize.width, self.item.originalSize.height);
|
||||
|
||||
if (cropOrientation != NULL)
|
||||
*cropOrientation = (adjustments != nil) ? adjustments.cropOrientation : UIImageOrientationUp;
|
||||
|
||||
if (cropMirrored != NULL)
|
||||
*cropMirrored = adjustments.cropMirrored;
|
||||
|
||||
return self.item.originalSize;
|
||||
}
|
||||
|
||||
- (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)setScrubberPlaying:(bool)value
|
||||
{
|
||||
if (_dotVideoView == nil) {
|
||||
AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player;
|
||||
_dotVideoView = [[TGModernGalleryVideoView alloc] initWithFrame:CGRectMake(0.0, 0.0, 27.0, 44.0) player:player];
|
||||
_dotVideoView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
_dotVideoView.hidden = true;
|
||||
[_scrubberView setDotVideoView:_dotVideoView];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)coverPosition {
|
||||
return _scrubberView.value;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)trimStartValue {
|
||||
return _scrubberView.trimStartValue;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)trimEndValue {
|
||||
return _scrubberView.trimEndValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -87,7 +87,7 @@
|
||||
[_portraitButton addTarget:self action:@selector(blurButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_portraitButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/BlurPortrait"], [UIColor whiteColor])];
|
||||
[_portraitButton setTitle:TGLocalized(@"PhotoEditor.BlurToolPortrait")];
|
||||
[_buttonsWrapper addSubview:_portraitButton];
|
||||
// [_buttonsWrapper addSubview:_portraitButton];
|
||||
|
||||
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
|
||||
_sliderView.alpha = 0.0f;
|
||||
@ -266,28 +266,9 @@
|
||||
{
|
||||
_titleLabel.frame = CGRectMake((self.frame.size.width - _titleLabel.frame.size.width) / 2, 10, _titleLabel.frame.size.width, _titleLabel.frame.size.height);
|
||||
|
||||
// _offButton.frame = CGRectMake(CGFloor(self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
|
||||
// _radialButton.frame = CGRectMake(self.frame.size.width / 2 - 75, self.frame.size.height / 2 - 42, 100, 100);
|
||||
// _linearButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 - 50), self.frame.size.height / 2 - 42, 100, 100);
|
||||
// _portraitButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 + self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
|
||||
|
||||
NSArray *buttons = @[_offButton, _radialButton, _linearButton, _portraitButton];
|
||||
|
||||
UIView *leftButton = buttons.firstObject;
|
||||
UIView *centerLeftButton = [buttons objectAtIndex:1];
|
||||
UIView *centerRightButton = [buttons objectAtIndex:2];
|
||||
UIView *rightButton = buttons.lastObject;
|
||||
|
||||
CGFloat offset = self.frame.size.height / 2 - 42;
|
||||
CGSize buttonSize = CGSizeMake(100.0, 100.0);
|
||||
|
||||
leftButton.frame = CGRectMake(CGFloor(self.frame.size.width / 8 * 1.5 - 3 - buttonSize.width / 2), offset, buttonSize.width, buttonSize.height);
|
||||
|
||||
centerLeftButton.frame = CGRectMake(CGFloor(self.frame.size.width / 10 * 3.75 + 5 - buttonSize.width / 2), offset, buttonSize.width, buttonSize.height);
|
||||
|
||||
centerRightButton.frame = CGRectMake(CGCeil(self.frame.size.width - centerLeftButton.frame.origin.x - buttonSize.width), offset, buttonSize.width, buttonSize.height);
|
||||
|
||||
rightButton.frame = CGRectMake(CGCeil(self.frame.size.width - leftButton.frame.origin.x - buttonSize.width), offset, buttonSize.width, buttonSize.height);
|
||||
_offButton.frame = CGRectMake(CGFloor(self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
|
||||
_radialButton.frame = CGRectMake(self.frame.size.width / 2 - 50, self.frame.size.height / 2 - 42, 100, 100);
|
||||
_linearButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 + self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
|
||||
|
||||
_sliderView.frame = CGRectMake(TGPhotoEditorSliderViewMargin, (self.frame.size.height - 32) / 2, self.frame.size.width - 2 * TGPhotoEditorSliderViewMargin, 32);
|
||||
}
|
||||
|
@ -18,7 +18,8 @@
|
||||
#import "TGProgressWindow.h"
|
||||
|
||||
#import "PGPhotoEditor.h"
|
||||
#import "PGEnhanceTool.h"
|
||||
#import "PGPhotoEditorView.h"
|
||||
|
||||
#import "TGPaintFaceDetector.h"
|
||||
|
||||
#import <LegacyComponents/PGPhotoEditorValues.h>
|
||||
@ -42,13 +43,14 @@
|
||||
#import "TGPhotoAvatarPreviewController.h"
|
||||
|
||||
#import "TGMessageImageViewOverlayView.h"
|
||||
#import "TGMediaPickerGalleryVideoScrubber.h"
|
||||
|
||||
#import "TGMenuSheetController.h"
|
||||
|
||||
#import <LegacyComponents/AVURLAsset+TGMediaItem.h>
|
||||
#import "TGCameraCapturedVideo.h"
|
||||
|
||||
@interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, UIDocumentInteractionControllerDelegate>
|
||||
@interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate, UIDocumentInteractionControllerDelegate>
|
||||
{
|
||||
bool _switchingTab;
|
||||
TGPhotoEditorTab _availableTabs;
|
||||
@ -75,8 +77,6 @@
|
||||
SMetaDisposable *_playerItemDisposable;
|
||||
id _playerStartedObserver;
|
||||
id _playerReachedEndObserver;
|
||||
bool _registeredKeypathObserver;
|
||||
NSTimer *_positionTimer;
|
||||
bool _scheduledVideoPlayback;
|
||||
|
||||
id<TGMediaEditAdjustments> _initialAdjustments;
|
||||
@ -97,6 +97,13 @@
|
||||
|
||||
SMetaDisposable *_faceDetectorDisposable;
|
||||
|
||||
bool _initializedScrubber;
|
||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||
PGPhotoEditorView *_dotVideoView;
|
||||
|
||||
bool _requestingThumbnails;
|
||||
SMetaDisposable *_thumbnailsDisposable;
|
||||
|
||||
id<LegacyComponentsContext> _context;
|
||||
}
|
||||
|
||||
@ -145,6 +152,8 @@
|
||||
_photoEditor.trimEndValue = videoAdjustments.trimEndValue;
|
||||
}
|
||||
|
||||
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
self.customAppearanceMethodsForwarding = true;
|
||||
}
|
||||
return self;
|
||||
@ -155,6 +164,7 @@
|
||||
[self stopVideoPlayback:true];
|
||||
[_actionHandle reset];
|
||||
[_faceDetectorDisposable dispose];
|
||||
[_thumbnailsDisposable dispose];
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
@ -297,6 +307,12 @@
|
||||
[_photoEditor setPreviewOutput:_previewView];
|
||||
[self updatePreviewView];
|
||||
|
||||
if (_intent == TGPhotoEditorControllerAvatarIntent && _item.isVideo) {
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f)];
|
||||
_scrubberView.dataSource = self;
|
||||
_scrubberView.delegate = self;
|
||||
}
|
||||
|
||||
[self detectFaces];
|
||||
|
||||
[self presentEditorTab:_currentTab];
|
||||
@ -364,6 +380,17 @@
|
||||
if ([_currentTabController isKindOfClass:[TGPhotoCropController class]])
|
||||
return;
|
||||
|
||||
_scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
|
||||
_scrubberView.hasDotPicker = true;
|
||||
_scrubberView.disableZoom = true;
|
||||
_scrubberView.disableTimeDisplay = true;
|
||||
_scrubberView.trimStartValue = 0.0;
|
||||
_scrubberView.trimEndValue = MIN(9.9, self.item.originalDuration);
|
||||
[_scrubberView setTrimApplied:self.item.originalDuration > 9.9];
|
||||
_scrubberView.maximumLength = 9.9;
|
||||
|
||||
[self setVideoEndTime:_scrubberView.trimEndValue];
|
||||
|
||||
NSTimeInterval position = 0;
|
||||
TGMediaVideoEditAdjustments *adjustments = [_photoEditor exportAdjustments];
|
||||
if ([adjustments isKindOfClass:[TGMediaVideoEditAdjustments class]])
|
||||
@ -481,6 +508,9 @@
|
||||
|
||||
- (void)_setupPlaybackReachedEndObserver
|
||||
{
|
||||
if (_playerReachedEndObserver != nil)
|
||||
[_player removeTimeObserver:_playerReachedEndObserver];
|
||||
|
||||
PGPhotoEditor *photoEditor = _photoEditor;
|
||||
CMTime endTime = CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100));
|
||||
if (photoEditor.trimEndValue > DBL_EPSILON && photoEditor.trimEndValue < CMTimeGetSeconds(_player.currentItem.duration))
|
||||
@ -496,9 +526,6 @@
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf->_player seekToTime:startTime];
|
||||
if ([strongSelf->_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
|
||||
[(TGPhotoAvatarPreviewController *)strongSelf->_currentTabController setScrubberPosition:photoEditor.trimStartValue reset:true];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
@ -518,17 +545,9 @@
|
||||
[_player.currentItem seekToTime:targetTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
|
||||
|
||||
[self _setupPlaybackStartedObserver];
|
||||
|
||||
if (!_registeredKeypathObserver) {
|
||||
[_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
|
||||
_registeredKeypathObserver = true;
|
||||
}
|
||||
}
|
||||
|
||||
[_player play];
|
||||
|
||||
_positionTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(positionTimerEvent) interval:0.25 repeat:true];
|
||||
[self positionTimerEvent];
|
||||
}
|
||||
|
||||
- (void)stopVideoPlayback:(bool)reset {
|
||||
@ -537,33 +556,8 @@
|
||||
[_player removeTimeObserver:_playerStartedObserver];
|
||||
if (_playerReachedEndObserver != nil)
|
||||
[_player removeTimeObserver:_playerReachedEndObserver];
|
||||
|
||||
if (_registeredKeypathObserver) {
|
||||
[_player removeObserver:self forKeyPath:@"rate" context:nil];
|
||||
_registeredKeypathObserver = false;
|
||||
}
|
||||
}
|
||||
[_player pause];
|
||||
|
||||
[_positionTimer invalidate];
|
||||
_positionTimer = nil;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)__unused change context:(void *)__unused context
|
||||
{
|
||||
if (object == _player && [keyPath isEqualToString:@"rate"])
|
||||
{
|
||||
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
|
||||
[(TGPhotoAvatarPreviewController *)_currentTabController setScrubberPlaying:_player.rate > FLT_EPSILON];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)positionTimerEvent
|
||||
{
|
||||
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
|
||||
[(TGPhotoAvatarPreviewController *)_currentTabController setScrubberPosition:CMTimeGetSeconds(_player.currentItem.currentTime) reset:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)seekVideo:(NSTimeInterval)position {
|
||||
@ -573,6 +567,7 @@
|
||||
|
||||
- (void)setVideoEndTime:(NSTimeInterval)endTime {
|
||||
_player.currentItem.forwardPlaybackEndTime = CMTimeMakeWithSeconds(endTime, NSEC_PER_SEC);
|
||||
[self _setupPlaybackReachedEndObserver];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
@ -1330,7 +1325,11 @@
|
||||
|
||||
case TGPhotoEditorPreviewTab:
|
||||
{
|
||||
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView];
|
||||
if ([_currentTabController isKindOfClass:[TGPhotoToolsController class]]) {
|
||||
[_scrubberView reloadThumbnails];
|
||||
}
|
||||
|
||||
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView];
|
||||
previewController.item = _item;
|
||||
previewController.toolbarLandscapeSize = TGPhotoEditorToolbarSize;
|
||||
previewController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView, bool *noTransitionView)
|
||||
@ -1338,6 +1337,10 @@
|
||||
*referenceFrame = transitionReferenceFrame;
|
||||
*parentView = transitionParentView;
|
||||
*noTransitionView = transitionNoTransitionView;
|
||||
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf startVideoPlayback:true];
|
||||
|
||||
return transitionReferenceView;
|
||||
};
|
||||
@ -1351,7 +1354,6 @@
|
||||
strongSelf.finishedTransitionIn();
|
||||
|
||||
strongSelf->_switchingTab = false;
|
||||
[strongSelf startVideoPlayback:true];
|
||||
};
|
||||
previewController.controlVideoPlayback = ^(bool play) {
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
@ -1635,10 +1637,9 @@
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(qualityController.preset) forKey:@"TG_preferredVideoPreset_v0"];
|
||||
} else if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]])
|
||||
{
|
||||
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
|
||||
videoStartValue = previewController.coverPosition;
|
||||
trimStartValue = previewController.trimStartValue;
|
||||
trimEndValue = previewController.trimEndValue;
|
||||
videoStartValue = _scrubberView.value;
|
||||
trimStartValue = _scrubberView.trimStartValue;
|
||||
trimEndValue = _scrubberView.trimEndValue;
|
||||
}
|
||||
|
||||
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
|
||||
@ -2034,6 +2035,21 @@
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
|
||||
portraitToolbarViewBottomEdge = screenEdges.bottom;
|
||||
_portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - safeAreaInset.bottom, referenceSize.width, TGPhotoEditorToolbarSize + safeAreaInset.bottom);
|
||||
|
||||
_scrubberView.frame = CGRectMake(0.0, 0.0, _portraitToolbarView.frame.size.width, _scrubberView.frame.size.height);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!_initializedScrubber) {
|
||||
[_scrubberView layoutSubviews];
|
||||
_initializedScrubber = true;
|
||||
[_scrubberView reloadData];
|
||||
[_scrubberView resetToStart];
|
||||
|
||||
[self updateDotImage];
|
||||
} else {
|
||||
[_scrubberView reloadThumbnails];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_setScreenImage:(UIImage *)screenImage
|
||||
@ -2167,4 +2183,247 @@
|
||||
return avatarTabs;
|
||||
}
|
||||
|
||||
#pragma mark - Video Scrubber Data Source & Delegate
|
||||
|
||||
#pragma mark Scrubbing
|
||||
|
||||
- (id<TGMediaEditableItem>)item {
|
||||
return _item;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)videoScrubberDuration:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
return self.item.originalDuration;
|
||||
}
|
||||
|
||||
- (CGFloat)videoScrubberThumbnailAspectRatio:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
if (CGSizeEqualToSize(self.item.originalSize, CGSizeZero))
|
||||
return 1.0f;
|
||||
|
||||
return self.item.originalSize.width / self.item.originalSize.height;
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidBeginScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
[self stopVideoPlayback:false];
|
||||
|
||||
if (_dotVideoView == nil) {
|
||||
_dotVideoView = [[PGPhotoEditorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 26.0, 44.0)];
|
||||
[_scrubberView setDotVideoView:_dotVideoView];
|
||||
|
||||
_photoEditor.additionalOutputs = @[_dotVideoView];
|
||||
} else {
|
||||
_dotVideoView.hidden = false;
|
||||
}
|
||||
|
||||
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
|
||||
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
|
||||
return;
|
||||
|
||||
[previewController beginScrubbing];
|
||||
}
|
||||
|
||||
- (void)updateDotImage {
|
||||
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
|
||||
AVPlayer *player = _player;
|
||||
[[SQueue concurrentDefaultQueue] dispatch:^{
|
||||
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];
|
||||
generator.appliesPreferredTrackTransform = true;
|
||||
generator.maximumSize = CGSizeMake(128.0f, 128.0f);
|
||||
generator.requestedTimeToleranceAfter = kCMTimeZero;
|
||||
generator.requestedTimeToleranceBefore = kCMTimeZero;
|
||||
CGImageRef imageRef = [generator copyCGImageAtTime:player.currentItem.currentTime actualTime:NULL error:NULL];
|
||||
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
|
||||
CGImageRelease(imageRef);
|
||||
|
||||
if (adjustments.toolsApplied) {
|
||||
PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true];
|
||||
editor.standalone = true;
|
||||
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
|
||||
image = editor.currentResultImage;
|
||||
}
|
||||
|
||||
TGDispatchOnMainThread(^{
|
||||
[_scrubberView setDotImage:image];
|
||||
_dotVideoView.hidden = true;
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
[self updateDotImage];
|
||||
|
||||
__weak TGPhotoEditorController *weakSelf = self;
|
||||
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
|
||||
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
|
||||
return;
|
||||
|
||||
[previewController endScrubbing:^bool{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return false;
|
||||
|
||||
return !strongSelf->_scrubberView.isScrubbing;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
|
||||
{
|
||||
[self seekVideo:position];
|
||||
}
|
||||
|
||||
#pragma mark Trimming
|
||||
|
||||
- (bool)hasTrimming
|
||||
{
|
||||
return _scrubberView.hasTrimming;
|
||||
}
|
||||
|
||||
- (CMTimeRange)trimRange
|
||||
{
|
||||
return CMTimeRangeMake(CMTimeMakeWithSeconds(_scrubberView.trimStartValue , NSEC_PER_SEC), CMTimeMakeWithSeconds((_scrubberView.trimEndValue - _scrubberView.trimStartValue), NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidBeginEditing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
[self stopVideoPlayback:false];
|
||||
|
||||
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
|
||||
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
|
||||
return;
|
||||
|
||||
[previewController setPlayButtonHidden:true animated:false];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidEndEditing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
|
||||
{
|
||||
[self setVideoEndTime:videoScrubber.trimEndValue];
|
||||
|
||||
[self seekVideo:videoScrubber.trimStartValue];
|
||||
[self stopVideoPlayback:false];
|
||||
|
||||
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
|
||||
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
|
||||
return;
|
||||
|
||||
[previewController setPlayButtonHidden:true animated:false];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
|
||||
{
|
||||
[self seekVideo:startValue];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingEndValueDidChange:(NSTimeInterval)endValue
|
||||
{
|
||||
[self seekVideo:endValue];
|
||||
}
|
||||
|
||||
#pragma mark Thumbnails
|
||||
|
||||
- (NSArray *)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber evenlySpacedTimestamps:(NSInteger)count startingAt:(NSTimeInterval)startTimestamp endingAt:(NSTimeInterval)endTimestamp
|
||||
{
|
||||
if (endTimestamp < startTimestamp)
|
||||
return nil;
|
||||
|
||||
if (count == 0)
|
||||
return nil;
|
||||
|
||||
NSTimeInterval duration = [self videoScrubberDuration:videoScrubber];
|
||||
if (endTimestamp > duration)
|
||||
endTimestamp = duration;
|
||||
|
||||
NSTimeInterval interval = (endTimestamp - startTimestamp) / count;
|
||||
|
||||
NSMutableArray *timestamps = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = 0; i < count; i++)
|
||||
[timestamps addObject:@(startTimestamp + i * interval)];
|
||||
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
|
||||
{
|
||||
if (timestamps.count == 0)
|
||||
return;
|
||||
|
||||
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
|
||||
|
||||
SSignal *thumbnailsSignal = nil;
|
||||
if ([self.item isKindOfClass:[TGMediaAsset class]]) {
|
||||
thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps];
|
||||
} else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||
thumbnailsSignal = [((TGCameraCapturedVideo *)self.item).avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
|
||||
return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps];
|
||||
}];
|
||||
}
|
||||
|
||||
_requestingThumbnails = true;
|
||||
|
||||
__weak TGPhotoEditorController *weakSelf = self;
|
||||
[_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) {
|
||||
if (adjustments.toolsApplied) {
|
||||
NSMutableArray *editedImages = [[NSMutableArray alloc] init];
|
||||
PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true];
|
||||
editor.standalone = true;
|
||||
for (UIImage *image in images) {
|
||||
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
|
||||
UIImage *resultImage = editor.currentResultImage;
|
||||
if (resultImage != nil) {
|
||||
[editedImages addObject:resultImage];
|
||||
} else {
|
||||
[editedImages addObject:image];
|
||||
}
|
||||
}
|
||||
return editedImages;
|
||||
} else {
|
||||
return images;
|
||||
}
|
||||
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *images)
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
|
||||
{
|
||||
if (index < timestamps.count)
|
||||
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] isSummaryThubmnail:isSummaryThumbnails];
|
||||
}];
|
||||
} completed:^
|
||||
{
|
||||
__strong TGPhotoEditorController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
strongSelf->_requestingThumbnails = false;
|
||||
}]];
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidFinishRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
_requestingThumbnails = false;
|
||||
}
|
||||
|
||||
- (void)videoScrubberDidCancelRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
|
||||
{
|
||||
_requestingThumbnails = false;
|
||||
}
|
||||
|
||||
- (CGSize)videoScrubberOriginalSize:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber cropRect:(CGRect *)cropRect cropOrientation:(UIImageOrientation *)cropOrientation cropMirrored:(bool *)cropMirrored
|
||||
{
|
||||
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
|
||||
if (cropRect != NULL)
|
||||
*cropRect = (adjustments != nil) ? adjustments.cropRect : CGRectMake(0, 0, self.item.originalSize.width, self.item.originalSize.height);
|
||||
|
||||
if (cropOrientation != NULL)
|
||||
*cropOrientation = (adjustments != nil) ? adjustments.cropOrientation : UIImageOrientationUp;
|
||||
|
||||
if (cropMirrored != NULL)
|
||||
*cropMirrored = adjustments.cropMirrored;
|
||||
|
||||
return self.item.originalSize;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
@ -164,6 +164,11 @@ typedef enum {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
{
|
||||
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];
|
||||
}
|
||||
|
||||
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
{
|
||||
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio];
|
||||
|
@ -373,8 +373,10 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
|
||||
{
|
||||
if (self.outputVideoFormatDescription == NULL)
|
||||
[self setupVideoPipelineWithInputFormatDescription:formatDescription];
|
||||
else
|
||||
[self renderVideoSampleBuffer:sampleBuffer];
|
||||
else {
|
||||
[_recorder appendVideoSampleBuffer:sampleBuffer];
|
||||
// [self renderVideoSampleBuffer:sampleBuffer];
|
||||
}
|
||||
}
|
||||
else if (connection == _audioConnection)
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
||||
|
||||
self.setMainButton = HighlightableButtonNode()
|
||||
self.setMainButton.isHidden = true
|
||||
self.setMainButton.setAttributedTitle(NSAttributedString(string: self.strings.ProfilePhoto_SetMain, font: Font.regular(17.0), textColor: .white), for: .normal)
|
||||
self.setMainButton.setAttributedTitle(NSAttributedString(string: self.strings.ProfilePhoto_SetMainPhoto, font: Font.regular(17.0), textColor: .white), for: .normal)
|
||||
|
||||
self.mainNode = ASTextNode()
|
||||
self.mainNode.maximumNumberOfLines = 1
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -4585,6 +4585,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
strongSelf.openScheduledMessages()
|
||||
}
|
||||
}, openPeersNearby: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = strongSelf.context.sharedContext.makePeersNearbyController(context: strongSelf.context)
|
||||
controller.navigationPresentation = .master
|
||||
strongSelf.effectiveNavigationController?.pushViewController(controller, animated: true, completion: { })
|
||||
}
|
||||
}, displaySearchResultsTooltip: { [weak self] node, nodeRect in
|
||||
if let strongSelf = self {
|
||||
strongSelf.searchResultsTooltipController?.dismiss()
|
||||
|
@ -128,6 +128,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
|
||||
|
||||
var displayName = ""
|
||||
let distance = interfaceState.peerNearbyData?.distance ?? 0
|
||||
|
||||
if let renderedPeer = interfaceState.renderedPeer {
|
||||
if let chatPeer = renderedPeer.peers[renderedPeer.peerId] {
|
||||
displayName = chatPeer.compactDisplayTitle
|
||||
@ -146,8 +147,18 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
|
||||
if let item = self.stickerItem {
|
||||
self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
|
||||
} else if !self.didSetupSticker {
|
||||
let sticker: Signal<TelegramMediaFile?, NoError>
|
||||
if let preloadedSticker = interfaceState.peerNearbyData?.sticker {
|
||||
sticker = .single(preloadedSticker)
|
||||
} else {
|
||||
sticker = randomGreetingSticker(account: self.account)
|
||||
|> map { item -> TelegramMediaFile? in
|
||||
return item?.file
|
||||
}
|
||||
}
|
||||
|
||||
self.didSetupSticker = true
|
||||
self.disposable.set((randomGreetingSticker(account: self.account)
|
||||
self.disposable.set((sticker
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sticker in
|
||||
if let strongSelf = self, let sticker = sticker {
|
||||
let inputNodeInteraction = ChatMediaInputNodeInteraction(
|
||||
@ -172,7 +183,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
|
||||
|
||||
let index = ItemCollectionItemIndex(index: 0, id: 0)
|
||||
let collectionId = ItemCollectionId(namespace: 0, id: 0)
|
||||
let stickerPackItem = StickerPackItem(index: index, file: sticker.file, indexKeys: [])
|
||||
let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: [])
|
||||
let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {})
|
||||
strongSelf.stickerItem = item
|
||||
strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
|
||||
|
@ -360,7 +360,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
|
||||
self.didSetUpAnimationNode = true
|
||||
let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
let fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
}
|
||||
}
|
||||
|
@ -118,9 +118,10 @@ final class ChatPanelInterfaceInteraction {
|
||||
let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
|
||||
let openScheduledMessages: () -> Void
|
||||
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
||||
let openPeersNearby: () -> Void
|
||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||
|
||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping (Bool) -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping (Bool) -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, openPeersNearby: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||
self.setupReplyMessage = setupReplyMessage
|
||||
self.setupEditMessage = setupEditMessage
|
||||
self.beginMessageSelection = beginMessageSelection
|
||||
@ -188,6 +189,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
self.displaySlowmodeTooltip = displaySlowmodeTooltip
|
||||
self.displaySendMessageOptions = displaySendMessageOptions
|
||||
self.openScheduledMessages = openScheduledMessages
|
||||
self.openPeersNearby = openPeersNearby
|
||||
self.displaySearchResultsTooltip = displaySearchResultsTooltip
|
||||
self.statuses = statuses
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
}, openPeersNearby: {
|
||||
}, displaySearchResultsTooltip: { _, _ in
|
||||
}, statuses: nil)
|
||||
|
||||
|
@ -186,7 +186,11 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
|
||||
private let labelNode: ImmediateTextNode
|
||||
private let filledBackgroundNode: LinkHighlightingNode
|
||||
|
||||
init(openPeer: @escaping () -> Void) {
|
||||
private let openPeersNearby: () -> Void
|
||||
|
||||
init(openPeersNearby: @escaping () -> Void) {
|
||||
self.openPeersNearby = openPeersNearby
|
||||
|
||||
self.labelNode = ImmediateTextNode()
|
||||
self.labelNode.maximumNumberOfLines = 1
|
||||
self.labelNode.textAlignment = .center
|
||||
@ -197,22 +201,17 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
|
||||
|
||||
self.addSubnode(self.filledBackgroundNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.labelNode.highlightAttributeAction = { attributes in
|
||||
for (key, _) in attributes {
|
||||
if key.rawValue == "_Link" {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
self.labelNode.tapAttributeAction = { attributes, _ in
|
||||
for (key, _) in attributes {
|
||||
if key.rawValue == "_Link" {
|
||||
openPeer()
|
||||
}
|
||||
}
|
||||
}
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
self.view.addGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
self.openPeersNearby()
|
||||
}
|
||||
|
||||
func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, distance: Int32, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
@ -233,7 +232,7 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
|
||||
let attributedString = NSMutableAttributedString(string: stringAndRanges.0, font: Font.regular(13.0), textColor: primaryTextColor)
|
||||
|
||||
let boldAttributes = [NSAttributedString.Key.font: Font.semibold(13.0), NSAttributedString.Key(rawValue: "_Link"): true as NSNumber]
|
||||
for (_, range) in stringAndRanges.1 {
|
||||
for (_, range) in stringAndRanges.1.prefix(1) {
|
||||
attributedString.addAttributes(boldAttributes, range: range)
|
||||
}
|
||||
|
||||
@ -438,8 +437,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
peerNearbyInfoNode = current
|
||||
} else {
|
||||
peerNearbyInfoTransition = .immediate
|
||||
peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeer: { [weak self] in
|
||||
self?.interfaceInteraction?.navigateToProfile(chatPeer.id)
|
||||
peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeersNearby: { [weak self] in
|
||||
self?.interfaceInteraction?.openPeersNearby()
|
||||
})
|
||||
self.addSubnode(peerNearbyInfoNode)
|
||||
self.peerNearbyInfoNode = peerNearbyInfoNode
|
||||
|
@ -148,7 +148,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
} else if ext == "wav" || ext == "opus" {
|
||||
return .audio(file)
|
||||
} else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 {
|
||||
if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let _ = LOTComposition(filePath: path) {
|
||||
if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let composition = LOTComposition(filePath: path), composition.timeDuration > 0.0 {
|
||||
let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
|
@ -37,6 +37,7 @@ import LocationUI
|
||||
import Geocoding
|
||||
import TextFormat
|
||||
import StatisticsUI
|
||||
import StickerResources
|
||||
|
||||
protocol PeerInfoScreenItem: class {
|
||||
var id: AnyHashable { get }
|
||||
@ -413,6 +414,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
}, openPeersNearby: {
|
||||
}, displaySearchResultsTooltip: { _, _ in
|
||||
}, statuses: nil)
|
||||
|
||||
@ -1092,6 +1094,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
private var didSetReady = false
|
||||
|
||||
private let preloadedSticker = Promise<TelegramMediaFile?>(nil)
|
||||
private let preloadStickerDisposable = MetaDisposable()
|
||||
|
||||
init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], ignoreGroupInCommon: PeerId?) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
@ -2011,6 +2016,27 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
strongSelf.updateData(data)
|
||||
})
|
||||
|
||||
if let _ = nearbyPeerDistance {
|
||||
self.preloadedSticker.set(.single(nil)
|
||||
|> then(randomGreetingSticker(account: context.account)
|
||||
|> map { item in
|
||||
return item?.file
|
||||
}))
|
||||
|
||||
self.preloadStickerDisposable.set((self.preloadedSticker.get()
|
||||
|> mapToSignal { sticker -> Signal<Void, NoError> in
|
||||
if let sticker = sticker {
|
||||
let _ = freeMediaFileInteractiveFetched(account: context.account, fileReference: .standalone(media: sticker)).start()
|
||||
return chatMessageAnimationData(postbox: context.account.postbox, resource: sticker.resource, fitzModifier: nil, width: 384, height: 384, synchronousLoad: false)
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}).start())
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -2023,6 +2049,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
self.updateAvatarDisposable.dispose()
|
||||
self.selectAddMemberDisposable.dispose()
|
||||
self.addMemberDisposable.dispose()
|
||||
self.preloadStickerDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -2206,7 +2233,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
switch key {
|
||||
case .message:
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) })))
|
||||
let _ = (self.preloadedSticker.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sticker in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
|
||||
}
|
||||
})
|
||||
}
|
||||
case .discussion:
|
||||
if let cachedData = self.data?.cachedData as? CachedChannelData, let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId {
|
||||
@ -2414,7 +2447,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
|
||||
private func openChatWithMessageSearch() {
|
||||
if let navigationController = (self.controller?.navigationController as? NavigationController) {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) })))
|
||||
let _ = (self.preloadedSticker.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sticker in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), activateMessageSearch: (.everything, ""), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2718,7 +2757,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
|
||||
private func openChat() {
|
||||
if let navigationController = self.controller?.navigationController as? NavigationController {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) })))
|
||||
let _ = (self.preloadedSticker.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sticker in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -548,6 +548,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
}, openPeersNearby: {
|
||||
}, displaySearchResultsTooltip: { _, _ in
|
||||
}, statuses: nil)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user