mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '32251e1ef2d2852be730292229767385bed0b2d7'
This commit is contained in:
commit
5f1d7970ce
@ -68,6 +68,8 @@ typedef enum {
|
|||||||
|
|
||||||
@property (nonatomic, strong) UIView<TGPhotoDrawingEntitiesView> *entitiesView;
|
@property (nonatomic, strong) UIView<TGPhotoDrawingEntitiesView> *entitiesView;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) bool ignoreCropForResult;
|
||||||
|
|
||||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id<TGMediaEditableItem>)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id<TGMediaEditAdjustments>)adjustments caption:(NSAttributedString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab;
|
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id<TGMediaEditableItem>)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id<TGMediaEditAdjustments>)adjustments caption:(NSAttributedString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab;
|
||||||
|
|
||||||
- (void)dismissEditor;
|
- (void)dismissEditor;
|
||||||
|
@ -46,6 +46,7 @@ CGSize TGPhotoThumbnailSizeForCurrentScreen();
|
|||||||
CGSize TGPhotoEditorScreenImageMaxSize();
|
CGSize TGPhotoEditorScreenImageMaxSize();
|
||||||
|
|
||||||
extern const CGSize TGPhotoEditorResultImageMaxSize;
|
extern const CGSize TGPhotoEditorResultImageMaxSize;
|
||||||
|
extern const CGSize TGPhotoEditorResultImageWallpaperMaxSize;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
|
|
||||||
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSAttributedString *)caption withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint adjustments:(bool)adjustments recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
|
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSAttributedString *)caption withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint adjustments:(bool)adjustments recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
|
||||||
|
|
||||||
+ (void)presentEditorWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
|
+ (void)presentEditorWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller withItem:(id<TGMediaEditableItem>)item cropRect:(CGRect)cropRect adjustments:(id<TGMediaEditAdjustments>)adjustments referenceView:(UIView *)referenceView completion:(void (^)(UIImage *, id<TGMediaEditAdjustments>))completion fullSizeCompletion:(void (^)(UIImage *))fullSizeCompletion beginTransitionOut:(void (^)())beginTransitionOut finishTransitionOut:(void (^)())finishTransitionOut;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -81,9 +81,6 @@
|
|||||||
[[NSFileManager defaultManager] removeItemAtPath:[self filePath] error:nil];
|
[[NSFileManager defaultManager] removeItemAtPath:[self filePath] error:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PGTick NSDate *startTime = [NSDate date]
|
|
||||||
#define PGTock NSLog(@"!=========== %s Time: %f", __func__, -[startTime timeIntervalSinceNow])
|
|
||||||
|
|
||||||
- (void)_saveToDisk:(UIImage *)image
|
- (void)_saveToDisk:(UIImage *)image
|
||||||
{
|
{
|
||||||
if (image == nil)
|
if (image == nil)
|
||||||
|
@ -982,14 +982,23 @@
|
|||||||
bool hasImageAdjustments = editorValues.toolsApplied || saveOnly;
|
bool hasImageAdjustments = editorValues.toolsApplied || saveOnly;
|
||||||
bool hasPainting = editorValues.hasPainting;
|
bool hasPainting = editorValues.hasPainting;
|
||||||
bool hasAnimation = editorValues.paintingData.hasAnimation;
|
bool hasAnimation = editorValues.paintingData.hasAnimation;
|
||||||
|
bool ignoreCropForResult = self.ignoreCropForResult;
|
||||||
|
|
||||||
SSignal *(^imageCropSignal)(UIImage *, bool) = ^(UIImage *image, bool resize)
|
SSignal *(^imageCropSignal)(UIImage *, bool) = ^(UIImage *image, bool resize)
|
||||||
{
|
{
|
||||||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
{
|
{
|
||||||
UIImage *paintingImage = !hasImageAdjustments ? editorValues.paintingData.image : nil;
|
if (ignoreCropForResult) {
|
||||||
UIImage *croppedImage = TGPhotoEditorCrop(image, paintingImage, photoEditor.cropOrientation, photoEditor.cropRotation, photoEditor.cropRect, photoEditor.cropMirrored, TGPhotoEditorResultImageMaxSize, photoEditor.originalSize, resize);
|
if (image.size.width > TGPhotoEditorResultImageWallpaperMaxSize.width || image.size.height > TGPhotoEditorResultImageWallpaperMaxSize.width) {
|
||||||
[subscriber putNext:croppedImage];
|
[subscriber putNext:TGPhotoEditorFitImage(image, TGPhotoEditorResultImageWallpaperMaxSize)];
|
||||||
|
} else {
|
||||||
|
[subscriber putNext:image];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UIImage *paintingImage = !hasImageAdjustments ? editorValues.paintingData.image : nil;
|
||||||
|
UIImage *croppedImage = TGPhotoEditorCrop(image, paintingImage, photoEditor.cropOrientation, photoEditor.cropRotation, photoEditor.cropRect, photoEditor.cropMirrored, TGPhotoEditorResultImageMaxSize, photoEditor.originalSize, resize);
|
||||||
|
[subscriber putNext:croppedImage];
|
||||||
|
}
|
||||||
[subscriber putCompletion];
|
[subscriber putCompletion];
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
@ -1045,12 +1054,18 @@
|
|||||||
void (^didFinishRenderingFullSizeImage)(UIImage *) = self.didFinishRenderingFullSizeImage;
|
void (^didFinishRenderingFullSizeImage)(UIImage *) = self.didFinishRenderingFullSizeImage;
|
||||||
void (^didFinishEditing)(id<TGMediaEditAdjustments>, UIImage *, UIImage *, bool , void(^)(void)) = self.didFinishEditing;
|
void (^didFinishEditing)(id<TGMediaEditAdjustments>, UIImage *, UIImage *, bool , void(^)(void)) = self.didFinishEditing;
|
||||||
|
|
||||||
|
TGPhotoEditorControllerIntent intent = _intent;
|
||||||
[[[[renderedImageSignal map:^id(UIImage *image)
|
[[[[renderedImageSignal map:^id(UIImage *image)
|
||||||
{
|
{
|
||||||
if (!hasImageAdjustments)
|
if (!hasImageAdjustments)
|
||||||
{
|
{
|
||||||
if (hasPainting && !hasAnimation && didFinishRenderingFullSizeImage != nil)
|
if (didFinishRenderingFullSizeImage != nil) {
|
||||||
didFinishRenderingFullSizeImage(image);
|
if (hasPainting && !hasAnimation) {
|
||||||
|
didFinishRenderingFullSizeImage(image);
|
||||||
|
} else if (intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
didFinishRenderingFullSizeImage(nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
@ -1155,6 +1170,13 @@
|
|||||||
_portraitToolbarView.alpha = 1.0f;
|
_portraitToolbarView.alpha = 1.0f;
|
||||||
_landscapeToolbarView.alpha = 1.0f;
|
_landscapeToolbarView.alpha = 1.0f;
|
||||||
} completion:nil];
|
} completion:nil];
|
||||||
|
|
||||||
|
if (_intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
[UIView animateWithDuration:0.25f delay:0.15 options:UIViewAnimationOptionCurveLinear animations:^
|
||||||
|
{
|
||||||
|
_backgroundView.alpha = 1.0f;
|
||||||
|
} completion:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)transitionOutSaving:(bool)saving completion:(void (^)(void))completion
|
- (void)transitionOutSaving:(bool)saving completion:(void (^)(void))completion
|
||||||
@ -1170,6 +1192,13 @@
|
|||||||
_landscapeToolbarView.alpha = 0.0f;
|
_landscapeToolbarView.alpha = 0.0f;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
if (_intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
[UIView animateWithDuration:0.1f animations:^
|
||||||
|
{
|
||||||
|
_backgroundView.alpha = 0.0f;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
_currentTabController.beginTransitionOut = self.beginTransitionOut;
|
_currentTabController.beginTransitionOut = self.beginTransitionOut;
|
||||||
[self setToolbarHidden:false animated:true];
|
[self setToolbarHidden:false animated:true];
|
||||||
|
|
||||||
|
@ -28,6 +28,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
UIView *_transitionInReferenceView;
|
UIView *_transitionInReferenceView;
|
||||||
UIView *_transitionInParentView;
|
UIView *_transitionInParentView;
|
||||||
CGRect _transitionTargetFrame;
|
CGRect _transitionTargetFrame;
|
||||||
|
|
||||||
|
UIView *_upperTransitionView;
|
||||||
|
CGRect _upperTransitionTargetFrame;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -114,6 +117,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
_dismissing = false;
|
_dismissing = false;
|
||||||
|
|
||||||
CGRect targetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame];
|
CGRect targetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame];
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
targetFrame = [self.view convertRect:targetFrame toView: parentView];
|
||||||
|
}
|
||||||
|
|
||||||
if (_CGRectEqualToRectWithEpsilon(targetFrame, referenceFrame, FLT_EPSILON))
|
if (_CGRectEqualToRectWithEpsilon(targetFrame, referenceFrame, FLT_EPSILON))
|
||||||
{
|
{
|
||||||
@ -157,12 +163,20 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
_transitionView = referenceView;
|
_transitionView = referenceView;
|
||||||
}
|
}
|
||||||
transitionViewSuperview = parentView;
|
transitionViewSuperview = parentView;
|
||||||
|
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
_upperTransitionView = [referenceView snapshotViewAfterScreenUpdates:false];
|
||||||
|
_upperTransitionView.alpha = 0.0;
|
||||||
|
_upperTransitionView.frame = [parentView convertRect:referenceFrame toView:self.view];
|
||||||
|
_upperTransitionTargetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame];
|
||||||
|
[self.view insertSubview:_upperTransitionView atIndex:0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_transitionView.hidden = false;
|
_transitionView.hidden = false;
|
||||||
_transitionView.frame = referenceFrame;
|
_transitionView.frame = referenceFrame;
|
||||||
_transitionTargetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame];
|
_transitionTargetFrame = targetFrame;
|
||||||
[transitionViewSuperview addSubview:_transitionView];
|
[transitionViewSuperview addSubview:_transitionView];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +188,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
_transitionInProgress = true;
|
_transitionInProgress = true;
|
||||||
|
|
||||||
CGAffineTransform initialTransform = _transitionView.transform;
|
CGAffineTransform initialTransform = _transitionView.transform;
|
||||||
|
[UIView animateWithDuration:0.25 animations:^{
|
||||||
|
_upperTransitionView.alpha = 1.0;
|
||||||
|
}];
|
||||||
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
|
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
|
||||||
{
|
{
|
||||||
if (_animateScale) {
|
if (_animateScale) {
|
||||||
@ -182,6 +199,7 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
_transitionView.transform = CGAffineTransformScale(initialTransform, scale, scale);
|
_transitionView.transform = CGAffineTransformScale(initialTransform, scale, scale);
|
||||||
} else {
|
} else {
|
||||||
_transitionView.frame = _transitionTargetFrame;
|
_transitionView.frame = _transitionTargetFrame;
|
||||||
|
_upperTransitionView.frame = _upperTransitionTargetFrame;
|
||||||
}
|
}
|
||||||
} completion:^(BOOL finished) {
|
} completion:^(BOOL finished) {
|
||||||
_transitionInProgress = false;
|
_transitionInProgress = false;
|
||||||
@ -201,6 +219,8 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self _finishedTransitionInWithView:transitionView];
|
[self _finishedTransitionInWithView:transitionView];
|
||||||
|
[_upperTransitionView removeFromSuperview];
|
||||||
|
_upperTransitionView = nil;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +295,11 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
|
|
||||||
if (saving)
|
if (saving)
|
||||||
{
|
{
|
||||||
[self _animatePreviewViewTransitionOutToFrame:CGRectNull saving:saving parentView:parentView completion:^
|
CGRect targetFrame = CGRectNull;
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
targetFrame = referenceFrame;
|
||||||
|
}
|
||||||
|
[self _animatePreviewViewTransitionOutToFrame:targetFrame saving:saving parentView:parentView completion:^
|
||||||
{
|
{
|
||||||
if (completion != nil)
|
if (completion != nil)
|
||||||
completion();
|
completion();
|
||||||
@ -316,6 +340,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
orientation = UIInterfaceOrientationPortrait;
|
orientation = UIInterfaceOrientationPortrait;
|
||||||
|
|
||||||
CGRect sourceFrame = [self transitionOutSourceFrameForReferenceFrame:referenceView.frame orientation:orientation];
|
CGRect sourceFrame = [self transitionOutSourceFrameForReferenceFrame:referenceView.frame orientation:orientation];
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
sourceFrame = [self.view convertRect:sourceFrame toView: parentView];
|
||||||
|
}
|
||||||
CGRect targetFrame = referenceFrame;
|
CGRect targetFrame = referenceFrame;
|
||||||
toTransitionView.frame = sourceFrame;
|
toTransitionView.frame = sourceFrame;
|
||||||
|
|
||||||
@ -334,7 +361,12 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
|||||||
};
|
};
|
||||||
|
|
||||||
[animations addObject:@1];
|
[animations addObject:@1];
|
||||||
[self _animatePreviewViewTransitionOutToFrame:targetFrame saving:saving parentView:nil completion:^
|
|
||||||
|
CGRect previewTargetFrame = targetFrame;
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
previewTargetFrame = [referenceView convertRect:referenceView.bounds toView:self.view];
|
||||||
|
}
|
||||||
|
[self _animatePreviewViewTransitionOutToFrame:previewTargetFrame saving:saving parentView:nil completion:^
|
||||||
{
|
{
|
||||||
onAnimationCompletion(@1);
|
onAnimationCompletion(@1);
|
||||||
}];
|
}];
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#import <Accelerate/Accelerate.h>
|
#import <Accelerate/Accelerate.h>
|
||||||
|
|
||||||
const CGSize TGPhotoEditorResultImageMaxSize = { 2560, 2560 };
|
const CGSize TGPhotoEditorResultImageMaxSize = { 1280, 1280 };
|
||||||
|
const CGSize TGPhotoEditorResultImageWallpaperMaxSize = { 2048, 2048 };
|
||||||
const CGSize TGPhotoEditorScreenImageHardLimitSize = { 1280, 1280 };
|
const CGSize TGPhotoEditorScreenImageHardLimitSize = { 1280, 1280 };
|
||||||
const CGSize TGPhotoEditorScreenImageHardLimitLegacySize = { 750, 750 };
|
const CGSize TGPhotoEditorScreenImageHardLimitLegacySize = { 750, 750 };
|
||||||
|
|
||||||
|
@ -454,24 +454,40 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
|||||||
UIView *snapshotView = nil;
|
UIView *snapshotView = nil;
|
||||||
POPSpringAnimation *snapshotAnimation = nil;
|
POPSpringAnimation *snapshotAnimation = nil;
|
||||||
|
|
||||||
if (saving && CGRectIsNull(targetFrame) && parentView != nil)
|
if (saving && parentView != nil)
|
||||||
{
|
{
|
||||||
snapshotView = [previewView snapshotViewAfterScreenUpdates:false];
|
if (CGRectIsNull(targetFrame)) {
|
||||||
snapshotView.frame = previewView.frame;
|
snapshotView = [previewView snapshotViewAfterScreenUpdates:false];
|
||||||
|
snapshotView.frame = previewView.frame;
|
||||||
CGSize fittedSize = TGScaleToSize(previewView.frame.size, self.view.frame.size);
|
|
||||||
targetFrame = CGRectMake((self.view.frame.size.width - fittedSize.width) / 2, (self.view.frame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
|
CGSize fittedSize = TGScaleToSize(previewView.frame.size, self.view.frame.size);
|
||||||
|
targetFrame = CGRectMake((self.view.frame.size.width - fittedSize.width) / 2, (self.view.frame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
|
||||||
[parentView addSubview:snapshotView];
|
|
||||||
|
[parentView addSubview:snapshotView];
|
||||||
snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
|
|
||||||
snapshotAnimation.fromValue = [NSValue valueWithCGRect:snapshotView.frame];
|
snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
|
||||||
snapshotAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
|
snapshotAnimation.fromValue = [NSValue valueWithCGRect:snapshotView.frame];
|
||||||
|
snapshotAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
|
||||||
|
} else if (self.intent == TGPhotoEditorControllerWallpaperIntent) {
|
||||||
|
snapshotView = [previewView snapshotViewAfterScreenUpdates:false];
|
||||||
|
snapshotView.frame = [self.view convertRect:previewView.frame toView:parentView];
|
||||||
|
|
||||||
|
[parentView addSubview:snapshotView];
|
||||||
|
|
||||||
|
snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
|
||||||
|
snapshotAnimation.fromValue = [NSValue valueWithCGRect:snapshotView.frame];
|
||||||
|
snapshotAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CGRect previewTargetFrame = targetFrame;
|
||||||
|
if (self.intent == TGPhotoEditorControllerWallpaperIntent && saving) {
|
||||||
|
previewTargetFrame = [parentView convertRect:targetFrame toView:self.view];
|
||||||
}
|
}
|
||||||
|
|
||||||
POPSpringAnimation *previewAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
|
POPSpringAnimation *previewAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
|
||||||
previewAnimation.fromValue = [NSValue valueWithCGRect:previewView.frame];
|
previewAnimation.fromValue = [NSValue valueWithCGRect:previewView.frame];
|
||||||
previewAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
|
previewAnimation.toValue = [NSValue valueWithCGRect:previewTargetFrame];
|
||||||
|
|
||||||
POPSpringAnimation *previewAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha];
|
POPSpringAnimation *previewAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha];
|
||||||
previewAlphaAnimation.fromValue = @(previewView.alpha);
|
previewAlphaAnimation.fromValue = @(previewView.alpha);
|
||||||
|
@ -283,7 +283,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)presentEditorWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed
|
+ (void)presentEditorWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller withItem:(id<TGMediaEditableItem>)item cropRect:(CGRect)cropRect adjustments:(id<TGMediaEditAdjustments>)adjustments referenceView:(UIView *)referenceView completion:(void (^)(UIImage *, id<TGMediaEditAdjustments>))completion fullSizeCompletion:(void (^)(UIImage *))fullSizeCompletion beginTransitionOut:(void (^)())beginTransitionOut finishTransitionOut:(void (^)())finishTransitionOut;
|
||||||
{
|
{
|
||||||
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
|
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
|
||||||
|
|
||||||
@ -291,40 +291,67 @@
|
|||||||
|
|
||||||
UIImage *thumbnailImage;
|
UIImage *thumbnailImage;
|
||||||
|
|
||||||
TGPhotoEditorController *editorController = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:item intent:TGPhotoEditorControllerWallpaperIntent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:TGPhotoEditorToolsTab selectedTab:TGPhotoEditorToolsTab];
|
NSDictionary *toolValues;
|
||||||
|
if (adjustments != nil) {
|
||||||
|
toolValues = adjustments.toolValues;
|
||||||
|
} else {
|
||||||
|
toolValues = @{};
|
||||||
|
}
|
||||||
|
PGPhotoEditorValues *editorValues = [PGPhotoEditorValues editorValuesWithOriginalSize:item.originalSize cropRect:cropRect cropRotation:0.0f cropOrientation:UIImageOrientationUp cropLockedAspectRatio:0.0 cropMirrored:false toolValues:toolValues paintingData:nil sendAsGif:false];
|
||||||
|
|
||||||
|
TGPhotoEditorController *editorController = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:item intent:TGPhotoEditorControllerWallpaperIntent adjustments:editorValues caption:nil screenImage:thumbnailImage availableTabs:TGPhotoEditorToolsTab selectedTab:TGPhotoEditorToolsTab];
|
||||||
editorController.editingContext = editingContext;
|
editorController.editingContext = editingContext;
|
||||||
editorController.dontHideStatusBar = true;
|
editorController.dontHideStatusBar = true;
|
||||||
|
editorController.ignoreCropForResult = true;
|
||||||
|
|
||||||
editorController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
|
CGRect fromRect = referenceView.frame;// [referenceView convertRect:referenceView.bounds toView:nil];
|
||||||
|
editorController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView)
|
||||||
{
|
{
|
||||||
*referenceFrame = fromRect;
|
*referenceFrame = fromRect;
|
||||||
|
*parentView = referenceView.superview;
|
||||||
UIImageView *imageView = [[UIImageView alloc] initWithFrame:fromRect];
|
//UIImageView *imageView = [[UIImageView alloc] initWithFrame:fromRect];
|
||||||
//imageView.image = image;
|
//imageView.image = image;
|
||||||
|
|
||||||
return imageView;
|
return referenceView;
|
||||||
};
|
};
|
||||||
|
|
||||||
editorController.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
|
editorController.beginTransitionOut = ^UIView *(CGRect *referenceFrame, UIView **parentView)
|
||||||
{
|
{
|
||||||
CGRect startFrame = CGRectZero;
|
CGRect startFrame = CGRectZero;
|
||||||
if (referenceFrame != NULL)
|
if (referenceFrame != NULL)
|
||||||
{
|
{
|
||||||
startFrame = *referenceFrame;
|
startFrame = *referenceFrame;
|
||||||
*referenceFrame = fromRect;
|
*referenceFrame = fromRect;
|
||||||
|
*parentView = referenceView.superview;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[strongSelf transitionBackFromResultControllerWithReferenceFrame:startFrame];
|
if (beginTransitionOut) {
|
||||||
|
beginTransitionOut();
|
||||||
|
}
|
||||||
|
|
||||||
return nil; //strongSelf->_previewView;
|
return referenceView;
|
||||||
|
};
|
||||||
|
|
||||||
|
__weak TGPhotoEditorController *weakController = editorController;
|
||||||
|
editorController.finishedTransitionOut = ^(bool saved) {
|
||||||
|
TGPhotoEditorController *strongGalleryController = weakController;
|
||||||
|
if (strongGalleryController != nil && strongGalleryController.overlayWindow == nil)
|
||||||
|
{
|
||||||
|
TGNavigationController *navigationController = (TGNavigationController *)strongGalleryController.navigationController;
|
||||||
|
TGOverlayControllerWindow *window = (TGOverlayControllerWindow *)navigationController.view.window;
|
||||||
|
if ([window isKindOfClass:[TGOverlayControllerWindow class]])
|
||||||
|
[window dismiss];
|
||||||
|
}
|
||||||
|
if (finishTransitionOut) {
|
||||||
|
finishTransitionOut();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
editorController.didFinishRenderingFullSizeImage = ^(UIImage *resultImage)
|
editorController.didFinishRenderingFullSizeImage = ^(UIImage *resultImage)
|
||||||
{
|
{
|
||||||
|
fullSizeCompletion(resultImage);
|
||||||
};
|
};
|
||||||
|
|
||||||
__weak TGPhotoEditorController *weakController = editorController;
|
|
||||||
editorController.didFinishEditing = ^(id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges, void(^commit)(void))
|
editorController.didFinishEditing = ^(id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges, void(^commit)(void))
|
||||||
{
|
{
|
||||||
if (!hasChanges)
|
if (!hasChanges)
|
||||||
@ -334,6 +361,8 @@
|
|||||||
if (strongController == nil)
|
if (strongController == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
completion(resultImage, adjustments);
|
||||||
|
|
||||||
};
|
};
|
||||||
editorController.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
|
editorController.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
|
||||||
{
|
{
|
||||||
@ -347,17 +376,7 @@
|
|||||||
|
|
||||||
editorController.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
editorController.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
||||||
{
|
{
|
||||||
if (editableItem.isVideo) {
|
return [editableItem originalImageSignal:position];
|
||||||
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:editorController];
|
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:editorController];
|
||||||
|
@ -63,9 +63,8 @@ public enum LegacyMediaEditorMode {
|
|||||||
case adjustments
|
case adjustments
|
||||||
}
|
}
|
||||||
|
|
||||||
public func legacyWallpaperEditor(context: AccountContext, image: UIImage, fromRect: CGRect, mainSnapshot: UIView, snapshots: [UIView], transitionCompletion: (() -> Void)?, completion: @escaping (UIImage, TGMediaEditAdjustments?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
|
||||||
let item = TGCameraCapturedPhoto(existing: image)
|
public func legacyWallpaperEditor(context: AccountContext, item: TGMediaEditableItem, cropRect: CGRect, adjustments: TGMediaEditAdjustments?, referenceView: UIView, beginTransitionOut: (() -> Void)?, finishTransitionOut: (() -> Void)?, completion: @escaping (UIImage?, TGMediaEditAdjustments?) -> Void, fullSizeCompletion: @escaping (UIImage?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
||||||
legacyController.blocksBackgroundWhenInOverlay = true
|
legacyController.blocksBackgroundWhenInOverlay = true
|
||||||
@ -85,17 +84,17 @@ public func legacyWallpaperEditor(context: AccountContext, image: UIImage, fromR
|
|||||||
|
|
||||||
present(legacyController, nil)
|
present(legacyController, nil)
|
||||||
|
|
||||||
TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, withItem: item, from: fromRect, mainSnapshot: mainSnapshot, snapshots: snapshots as [Any], completion: { item, editingContext in
|
TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, with: item, cropRect: cropRect, adjustments: adjustments, referenceView: referenceView, completion: { image, adjustments in
|
||||||
let adjustments = editingContext?.adjustments(for: item)
|
completion(image, adjustments)
|
||||||
if let imageSignal = editingContext?.fullSizeImageUrl(for: item) {
|
}, fullSizeCompletion: { image in
|
||||||
imageSignal.start(next: { value in
|
Queue.mainQueue().async {
|
||||||
if let value = value as? NSURL, let data = try? Data(contentsOf: value as URL), let image = UIImage(data: data) {
|
fullSizeCompletion(image)
|
||||||
completion(image, adjustments)
|
|
||||||
}
|
|
||||||
}, error: { _ in }, completed: {})
|
|
||||||
}
|
}
|
||||||
}, dismissed: { [weak legacyController] in
|
}, beginTransitionOut: {
|
||||||
|
beginTransitionOut?()
|
||||||
|
}, finishTransitionOut: { [weak legacyController] in
|
||||||
legacyController?.dismiss()
|
legacyController?.dismiss()
|
||||||
|
finishTransitionOut?()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
|
|||||||
|
|
||||||
var intensity: Int32?
|
var intensity: Int32?
|
||||||
if let brightness {
|
if let brightness {
|
||||||
intensity = max(1, Int32(brightness * 100.0))
|
intensity = max(0, min(100, Int32(brightness * 100.0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: intensity)
|
let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: intensity)
|
||||||
@ -318,3 +318,77 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
|
|||||||
return croppedImage
|
return croppedImage
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LegacyWallpaperItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem {
|
||||||
|
var isVideo: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var uniqueIdentifier: String! {
|
||||||
|
return self.asset.localIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
let asset: PHAsset
|
||||||
|
let screenImage: UIImage
|
||||||
|
private(set) var thumbnailResource: TelegramMediaResource?
|
||||||
|
private(set) var imageResource: TelegramMediaResource?
|
||||||
|
let dimensions: CGSize
|
||||||
|
|
||||||
|
|
||||||
|
init(asset: PHAsset, screenImage: UIImage, dimensions: CGSize) {
|
||||||
|
self.asset = asset
|
||||||
|
self.screenImage = screenImage
|
||||||
|
self.dimensions = dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalSize: CGSize {
|
||||||
|
return self.dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
func thumbnailImageSignal() -> SSignal! {
|
||||||
|
return SSignal.complete()
|
||||||
|
// return SSignal(generator: { subscriber -> SDisposable? in
|
||||||
|
// let disposable = self.thumbnailImage.start(next: { image in
|
||||||
|
// subscriber.putNext(image)
|
||||||
|
// subscriber.putCompletion()
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// return SBlockDisposable(block: {
|
||||||
|
// disposable.dispose()
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
func screenImageSignal(_ position: TimeInterval) -> SSignal! {
|
||||||
|
return SSignal.single(self.screenImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalImage: Signal<UIImage, NoError> {
|
||||||
|
return fetchPhotoLibraryImage(localIdentifier: self.asset.localIdentifier, thumbnail: false)
|
||||||
|
|> filter { value in
|
||||||
|
return !(value?.1 ?? true)
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<UIImage, NoError> in
|
||||||
|
if let result = result {
|
||||||
|
return .single(result.0)
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func originalImageSignal(_ position: TimeInterval) -> SSignal! {
|
||||||
|
return SSignal(generator: { subscriber -> SDisposable? in
|
||||||
|
let disposable = self.originalImage.start(next: { image in
|
||||||
|
subscriber.putNext(image)
|
||||||
|
if !image.degraded() {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return SBlockDisposable(block: {
|
||||||
|
disposable.dispose()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import Display
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
|
||||||
final class WallpaperCropNode: ASDisplayNode, UIScrollViewDelegate {
|
final class WallpaperCropNode: ASDisplayNode, UIScrollViewDelegate {
|
||||||
private let scrollNode: ASScrollNode
|
let scrollNode: ASScrollNode
|
||||||
|
|
||||||
private var ignoreZoom = false
|
private var ignoreZoom = false
|
||||||
private var ignoreZoomTransition: ContainedViewLayoutTransition?
|
private var ignoreZoomTransition: ContainedViewLayoutTransition?
|
||||||
|
@ -167,10 +167,16 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WallpaperGalleryInteraction {
|
class WallpaperGalleryInteraction {
|
||||||
let editMedia: (UIImage, CGRect, UIView, [UIView], @escaping (UIImage, TGMediaEditAdjustments?) -> Void) -> Void
|
let editMedia: (PHAsset, UIImage, CGRect, TGMediaEditAdjustments?, UIView, @escaping (UIImage?, TGMediaEditAdjustments?) -> Void, @escaping (UIImage?) -> Void) -> Void
|
||||||
|
let beginTransitionToEditor: () -> Void
|
||||||
|
let beginTransitionFromEditor: () -> Void
|
||||||
|
let finishTransitionFromEditor: () -> Void
|
||||||
|
|
||||||
init(editMedia: @escaping (UIImage, CGRect, UIView, [UIView], @escaping (UIImage, TGMediaEditAdjustments?) -> Void) -> Void) {
|
init(editMedia: @escaping (PHAsset, UIImage, CGRect, TGMediaEditAdjustments?, UIView, @escaping (UIImage?, TGMediaEditAdjustments?) -> Void, @escaping (UIImage?) -> Void) -> Void, beginTransitionToEditor: @escaping () -> Void, beginTransitionFromEditor: @escaping () -> Void, finishTransitionFromEditor: @escaping () -> Void) {
|
||||||
self.editMedia = editMedia
|
self.editMedia = editMedia
|
||||||
|
self.beginTransitionToEditor = beginTransitionToEditor
|
||||||
|
self.beginTransitionFromEditor = beginTransitionFromEditor
|
||||||
|
self.finishTransitionFromEditor = finishTransitionFromEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,25 +248,50 @@ public class WallpaperGalleryController: ViewController {
|
|||||||
//self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
//self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
|
|
||||||
self.interaction = WallpaperGalleryInteraction(editMedia: { [weak self] image, fromRect, mainSnapshot, snapshots, apply in
|
self.interaction = WallpaperGalleryInteraction(editMedia: { [weak self] asset, image, cropRect, adjustments, referenceView, apply, fullSizeApply in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var snapshots = snapshots
|
let item = LegacyWallpaperItem(asset: asset, screenImage: image, dimensions: CGSize(width: asset.pixelWidth, height: asset.pixelHeight))
|
||||||
if let toolbarNode = self.toolbarNode, let snapshotView = toolbarNode.view.snapshotContentTree() {
|
legacyWallpaperEditor(context: context, item: item, cropRect: cropRect, adjustments: adjustments, referenceView: referenceView, beginTransitionOut: { [weak self] in
|
||||||
snapshotView.frame = toolbarNode.view.convert(toolbarNode.view.bounds, to: nil)
|
self?.interaction?.beginTransitionFromEditor()
|
||||||
snapshots.append(snapshotView)
|
}, finishTransitionOut: { [weak self] in
|
||||||
}
|
self?.interaction?.finishTransitionFromEditor()
|
||||||
|
|
||||||
legacyWallpaperEditor(context: context, image: image, fromRect: fromRect, mainSnapshot: mainSnapshot, snapshots: snapshots, transitionCompletion: {
|
|
||||||
|
|
||||||
}, completion: { image, adjustments in
|
}, completion: { image, adjustments in
|
||||||
apply(image, adjustments)
|
apply(image, adjustments)
|
||||||
|
}, fullSizeCompletion: { image in
|
||||||
|
fullSizeApply(image)
|
||||||
}, present: { [weak self] c, a in
|
}, present: { [weak self] c, a in
|
||||||
if let self {
|
if let self {
|
||||||
self.present(c, in: .window(.root))
|
self.present(c, in: .window(.root))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}, beginTransitionToEditor: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
|
||||||
|
if let toolbarNode = self.toolbarNode {
|
||||||
|
transition.updateAlpha(node: toolbarNode, alpha: 0.0)
|
||||||
|
}
|
||||||
|
}, beginTransitionFromEditor: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
|
||||||
|
if let toolbarNode = self.toolbarNode {
|
||||||
|
transition.updateAlpha(node: toolbarNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
if let centralItemNode = self.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode {
|
||||||
|
centralItemNode.beginTransitionFromEditor()
|
||||||
|
}
|
||||||
|
}, finishTransitionFromEditor: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let centralItemNode = self.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode {
|
||||||
|
centralItemNode.finishTransitionFromEditor()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var entries: [WallpaperGalleryEntry] = []
|
var entries: [WallpaperGalleryEntry] = []
|
||||||
@ -491,7 +522,7 @@ public class WallpaperGalleryController: ViewController {
|
|||||||
let entry = strongSelf.entries[centralItemNode.index]
|
let entry = strongSelf.entries[centralItemNode.index]
|
||||||
|
|
||||||
if case .peer = strongSelf.mode {
|
if case .peer = strongSelf.mode {
|
||||||
strongSelf.apply?(entry, options, centralItemNode.editedImage, centralItemNode.cropRect, centralItemNode.brightness)
|
strongSelf.apply?(entry, options, centralItemNode.editedFullSizeImage, centralItemNode.editedCropRect, centralItemNode.brightness)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
|
|
||||||
let wrapperNode: ASDisplayNode
|
let wrapperNode: ASDisplayNode
|
||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
|
private let temporaryImageNode: ASImageNode
|
||||||
let nativeNode: WallpaperBackgroundNode
|
let nativeNode: WallpaperBackgroundNode
|
||||||
let brightnessNode: ASDisplayNode
|
let brightnessNode: ASDisplayNode
|
||||||
private let statusNode: RadialStatusNode
|
private let statusNode: RadialStatusNode
|
||||||
@ -110,6 +111,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
private let dayNightButtonNode: WallpaperNavigationButtonNode
|
private let dayNightButtonNode: WallpaperNavigationButtonNode
|
||||||
private let editButtonNode: WallpaperNavigationButtonNode
|
private let editButtonNode: WallpaperNavigationButtonNode
|
||||||
|
|
||||||
|
private let buttonsContainerNode: SparseNode
|
||||||
private let blurButtonNode: WallpaperOptionButtonNode
|
private let blurButtonNode: WallpaperOptionButtonNode
|
||||||
private let motionButtonNode: WallpaperOptionButtonNode
|
private let motionButtonNode: WallpaperOptionButtonNode
|
||||||
private let patternButtonNode: WallpaperOptionButtonNode
|
private let patternButtonNode: WallpaperOptionButtonNode
|
||||||
@ -159,6 +161,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.wrapperNode = ASDisplayNode()
|
self.wrapperNode = ASDisplayNode()
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.contentAnimations = .subsequentUpdates
|
self.imageNode.contentAnimations = .subsequentUpdates
|
||||||
|
self.temporaryImageNode = ASImageNode()
|
||||||
|
self.temporaryImageNode.isUserInteractionEnabled = false
|
||||||
self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.cropNode = WallpaperCropNode()
|
self.cropNode = WallpaperCropNode()
|
||||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
||||||
@ -173,6 +177,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||||
self.messagesContainerNode.isUserInteractionEnabled = false
|
self.messagesContainerNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.buttonsContainerNode = SparseNode()
|
||||||
self.blurButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Blurred, value: .check(false))
|
self.blurButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Blurred, value: .check(false))
|
||||||
self.blurButtonNode.setEnabled(false)
|
self.blurButtonNode.setEnabled(false)
|
||||||
self.motionButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Motion, value: .check(false))
|
self.motionButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Motion, value: .check(false))
|
||||||
@ -248,20 +253,22 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.imageNode.clipsToBounds = true
|
self.imageNode.clipsToBounds = true
|
||||||
|
|
||||||
self.addSubnode(self.wrapperNode)
|
self.addSubnode(self.wrapperNode)
|
||||||
|
self.addSubnode(self.temporaryImageNode)
|
||||||
//self.addSubnode(self.statusNode)
|
//self.addSubnode(self.statusNode)
|
||||||
self.addSubnode(self.serviceBackgroundNode)
|
self.addSubnode(self.serviceBackgroundNode)
|
||||||
self.addSubnode(self.messagesContainerNode)
|
self.addSubnode(self.messagesContainerNode)
|
||||||
|
self.addSubnode(self.buttonsContainerNode)
|
||||||
|
|
||||||
self.addSubnode(self.blurButtonNode)
|
self.buttonsContainerNode.addSubnode(self.blurButtonNode)
|
||||||
self.addSubnode(self.motionButtonNode)
|
self.buttonsContainerNode.addSubnode(self.motionButtonNode)
|
||||||
self.addSubnode(self.patternButtonNode)
|
self.buttonsContainerNode.addSubnode(self.patternButtonNode)
|
||||||
self.addSubnode(self.colorsButtonNode)
|
self.buttonsContainerNode.addSubnode(self.colorsButtonNode)
|
||||||
self.addSubnode(self.playButtonNode)
|
self.buttonsContainerNode.addSubnode(self.playButtonNode)
|
||||||
self.addSubnode(self.sliderNode)
|
self.buttonsContainerNode.addSubnode(self.sliderNode)
|
||||||
self.addSubnode(self.cancelButtonNode)
|
self.buttonsContainerNode.addSubnode(self.cancelButtonNode)
|
||||||
self.addSubnode(self.shareButtonNode)
|
self.buttonsContainerNode.addSubnode(self.shareButtonNode)
|
||||||
self.addSubnode(self.dayNightButtonNode)
|
self.buttonsContainerNode.addSubnode(self.dayNightButtonNode)
|
||||||
self.addSubnode(self.editButtonNode)
|
self.buttonsContainerNode.addSubnode(self.editButtonNode)
|
||||||
|
|
||||||
self.imageNode.addSubnode(self.brightnessNode)
|
self.imageNode.addSubnode(self.brightnessNode)
|
||||||
|
|
||||||
@ -295,25 +302,31 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
}
|
}
|
||||||
switch entry {
|
switch entry {
|
||||||
case .asset, .contextResult:
|
case .asset, .contextResult:
|
||||||
if let editedImage = self.editedImage {
|
return self.cropNode.cropRect
|
||||||
let scale = editedImage.size.height / self.cropNode.cropRect.height
|
|
||||||
let cropRect = self.cropNode.cropRect
|
|
||||||
return CGRect(origin: CGPoint(x: cropRect.minX * scale, y: cropRect.minY * scale), size: CGSize(width: cropRect.width * scale, height: cropRect.height * scale))
|
|
||||||
} else {
|
|
||||||
return self.cropNode.cropRect
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var editedCropRect: CGRect? {
|
||||||
|
guard let cropRect = self.cropRect, let contentSize = self.contentSize else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if let editedFullSizeImage = self.editedFullSizeImage {
|
||||||
|
let scale = editedFullSizeImage.size.height / contentSize.height
|
||||||
|
return CGRect(origin: CGPoint(x: cropRect.minX * scale, y: cropRect.minY * scale), size: CGSize(width: cropRect.width * scale, height: cropRect.height * scale))
|
||||||
|
} else {
|
||||||
|
return cropRect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var brightness: CGFloat? {
|
var brightness: CGFloat? {
|
||||||
guard let entry = self.entry else {
|
guard let entry = self.entry else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch entry {
|
switch entry {
|
||||||
case .asset, .contextResult:
|
case .asset, .contextResult:
|
||||||
return self.sliderNode.value
|
return 1.0 - self.sliderNode.value
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -467,67 +480,56 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func editPressed() {
|
@objc private func editPressed() {
|
||||||
guard let image = self.imageNode.image else {
|
guard let image = self.imageNode.image, case let .asset(asset) = self.entry else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let originalImage = self.originalImage ?? image
|
let originalImage = self.originalImage ?? image
|
||||||
|
guard let cropRect = self.cropRect else {
|
||||||
var nodesToSnapshot = [
|
return
|
||||||
self.cancelButtonNode,
|
}
|
||||||
self.editButtonNode,
|
self.interaction?.editMedia(asset, originalImage, cropRect, self.currentAdjustments, self.cropNode.view, { [weak self] result, adjustments in
|
||||||
self.blurButtonNode,
|
guard let self else {
|
||||||
self.motionButtonNode,
|
return
|
||||||
self.serviceBackgroundNode
|
|
||||||
]
|
|
||||||
|
|
||||||
if let messageNodes = self.messageNodes {
|
|
||||||
for node in messageNodes {
|
|
||||||
nodesToSnapshot.append(node)
|
|
||||||
}
|
}
|
||||||
}
|
self.originalImage = originalImage
|
||||||
|
self.editedImage = result
|
||||||
var snapshots: [UIView] = []
|
self.currentAdjustments = adjustments
|
||||||
for node in nodesToSnapshot {
|
|
||||||
if let snapshotView = node.view.snapshotContentTree() {
|
|
||||||
snapshotView.frame = node.view.convert(node.view.bounds, to: nil)
|
|
||||||
snapshots.append(snapshotView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let snapshotView = self.dayNightButtonNode.view.snapshotView(afterScreenUpdates: false) {
|
|
||||||
snapshotView.frame = self.dayNightButtonNode.view.convert(self.dayNightButtonNode.view.bounds, to: nil)
|
|
||||||
snapshots.append(snapshotView)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mainSnapshotView: UIView
|
|
||||||
if let snapshotView = self.imageNode.view.snapshotView(afterScreenUpdates: false) {
|
|
||||||
snapshotView.frame = self.imageNode.view.convert(self.imageNode.view.bounds, to: nil)
|
|
||||||
mainSnapshotView = snapshotView
|
|
||||||
} else {
|
|
||||||
mainSnapshotView = UIView()
|
|
||||||
}
|
|
||||||
|
|
||||||
let fromRect = self.imageNode.view.convert(self.imageNode.bounds, to: nil)
|
|
||||||
self.interaction?.editMedia(originalImage, fromRect, mainSnapshotView, snapshots, { [weak self] result, adjustments in
|
|
||||||
self?.originalImage = originalImage
|
|
||||||
self?.editedImage = result
|
|
||||||
self?.currentAdjustments = adjustments
|
|
||||||
|
|
||||||
self?.imageNode.setSignal(.single({ arguments in
|
self.imageNode.setSignal(.single({ arguments in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, opaque: false)
|
let context = DrawingContext(size: arguments.drawingSize, opaque: false)
|
||||||
context?.withFlippedContext({ context in
|
context?.withFlippedContext({ context in
|
||||||
if let cgImage = result.cgImage {
|
let image = result ?? originalImage
|
||||||
|
if let cgImage = image.cgImage {
|
||||||
context.draw(cgImage, in: CGRect(origin: .zero, size: arguments.drawingSize))
|
context.draw(cgImage, in: CGRect(origin: .zero, size: arguments.drawingSize))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
self.temporaryImageNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||||
|
self?.temporaryImageNode.image = nil
|
||||||
|
self?.temporaryImageNode.layer.removeAllAnimations()
|
||||||
|
})
|
||||||
|
}, { [weak self] image in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.editedFullSizeImage = image
|
||||||
|
|
||||||
|
self.temporaryImageNode.frame = self.imageNode.view.convert(self.imageNode.bounds, to: self.view)
|
||||||
|
self.temporaryImageNode.image = image ?? originalImage
|
||||||
|
|
||||||
|
if self.cropNode.isHidden {
|
||||||
|
self.temporaryImageNode.alpha = 0.0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.beginTransitionToEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var originalImage: UIImage?
|
private var originalImage: UIImage?
|
||||||
public private(set) var editedImage: UIImage?
|
public private(set) var editedImage: UIImage?
|
||||||
|
public private(set) var editedFullSizeImage: UIImage?
|
||||||
private var currentAdjustments: TGMediaEditAdjustments?
|
private var currentAdjustments: TGMediaEditAdjustments?
|
||||||
|
|
||||||
private func animateIntensityChange(delay: Double) {
|
private func animateIntensityChange(delay: Double) {
|
||||||
@ -557,6 +559,29 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func beginTransitionToEditor() {
|
||||||
|
self.cropNode.isHidden = true
|
||||||
|
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
|
||||||
|
transition.updateAlpha(node: self.messagesContainerNode, alpha: 0.0)
|
||||||
|
transition.updateAlpha(node: self.buttonsContainerNode, alpha: 0.0)
|
||||||
|
transition.updateAlpha(node: self.serviceBackgroundNode, alpha: 0.0)
|
||||||
|
|
||||||
|
self.interaction?.beginTransitionToEditor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginTransitionFromEditor() {
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
|
||||||
|
transition.updateAlpha(node: self.messagesContainerNode, alpha: 1.0)
|
||||||
|
transition.updateAlpha(node: self.buttonsContainerNode, alpha: 1.0)
|
||||||
|
transition.updateAlpha(node: self.serviceBackgroundNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishTransitionFromEditor() {
|
||||||
|
self.cropNode.isHidden = false
|
||||||
|
self.temporaryImageNode.alpha = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
@objc private func cancelPressed() {
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
@ -579,11 +604,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
var showPreviewTooltip = false
|
var showPreviewTooltip = false
|
||||||
|
|
||||||
if self.entry != entry || self.arguments.colorPreview != previousArguments.colorPreview {
|
if self.entry != entry || self.arguments.colorPreview != previousArguments.colorPreview {
|
||||||
let previousEntry = self.entry
|
|
||||||
self.entry = entry
|
self.entry = entry
|
||||||
if previousEntry != entry {
|
|
||||||
self.preparePatternEditing()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.colorsButtonNode.colors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors
|
self.colorsButtonNode.colors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors
|
||||||
|
|
||||||
@ -1204,9 +1225,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparePatternEditing() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func setMotionEnabled(_ enabled: Bool, animated: Bool) {
|
func setMotionEnabled(_ enabled: Bool, animated: Bool) {
|
||||||
if enabled {
|
if enabled {
|
||||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||||
@ -1606,6 +1624,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.wrapperNode.bounds = CGRect(origin: CGPoint(), size: layout.size)
|
self.wrapperNode.bounds = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
self.updateWrapperLayout(layout: layout, offset: offset, transition: transition)
|
self.updateWrapperLayout(layout: layout, offset: offset, transition: transition)
|
||||||
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
self.buttonsContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
|
||||||
if self.cropNode.supernode == nil {
|
if self.cropNode.supernode == nil {
|
||||||
self.imageNode.frame = self.wrapperNode.bounds
|
self.imageNode.frame = self.wrapperNode.bounds
|
||||||
|
@ -13,23 +13,17 @@ public final class PeerMediaUploadingItem: Equatable {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PreviousState: Equatable {
|
|
||||||
case wallpaper(TelegramWallpaper?)
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Content: Equatable {
|
public enum Content: Equatable {
|
||||||
case wallpaper(TelegramWallpaper)
|
case wallpaper(TelegramWallpaper)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let content: Content
|
public let content: Content
|
||||||
public let messageId: EngineMessage.Id?
|
public let messageId: EngineMessage.Id?
|
||||||
public let previousState: PreviousState?
|
|
||||||
public let progress: Float
|
public let progress: Float
|
||||||
|
|
||||||
init(content: Content, messageId: EngineMessage.Id?, previousState: PreviousState?, progress: Float) {
|
init(content: Content, messageId: EngineMessage.Id?, progress: Float) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.messageId = messageId
|
self.messageId = messageId
|
||||||
self.previousState = previousState
|
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +34,6 @@ public final class PeerMediaUploadingItem: Equatable {
|
|||||||
if lhs.messageId != rhs.messageId {
|
if lhs.messageId != rhs.messageId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.previousState != rhs.previousState {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.progress != rhs.progress {
|
if lhs.progress != rhs.progress {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -50,15 +41,11 @@ public final class PeerMediaUploadingItem: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withMessageId(_ messageId: EngineMessage.Id) -> PeerMediaUploadingItem {
|
func withMessageId(_ messageId: EngineMessage.Id) -> PeerMediaUploadingItem {
|
||||||
return PeerMediaUploadingItem(content: self.content, messageId: messageId, previousState: self.previousState, progress: self.progress)
|
return PeerMediaUploadingItem(content: self.content, messageId: messageId, progress: self.progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withProgress(_ progress: Float) -> PeerMediaUploadingItem {
|
func withProgress(_ progress: Float) -> PeerMediaUploadingItem {
|
||||||
return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, previousState: self.previousState, progress: progress)
|
return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, progress: progress)
|
||||||
}
|
|
||||||
|
|
||||||
func withPreviousState(_ previousState: PreviousState?) -> PeerMediaUploadingItem {
|
|
||||||
return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, previousState: previousState, progress: self.progress)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,38 +116,6 @@ private func generatePeerMediaMessage(network: Network, accountPeerId: EnginePee
|
|||||||
return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media)
|
return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparePeerMediaUpload(transaction: Transaction, peerId: EnginePeer.Id, content: PeerMediaUploadingItem.Content) -> PeerMediaUploadingItem.PreviousState? {
|
|
||||||
var previousState: PeerMediaUploadingItem.PreviousState?
|
|
||||||
switch content {
|
|
||||||
case let .wallpaper(wallpaper):
|
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
|
|
||||||
if let cachedData = cachedData as? CachedUserData {
|
|
||||||
previousState = .wallpaper(cachedData.wallpaper)
|
|
||||||
return cachedData.withUpdatedWallpaper(wallpaper)
|
|
||||||
} else {
|
|
||||||
return cachedData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return previousState
|
|
||||||
}
|
|
||||||
|
|
||||||
private func cancelPeerMediaUpload(transaction: Transaction, peerId: EnginePeer.Id, previousState: PeerMediaUploadingItem.PreviousState?) {
|
|
||||||
guard let previousState = previousState else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch previousState {
|
|
||||||
case let .wallpaper(previousWallpaper):
|
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
|
|
||||||
if let cachedData = cachedData as? CachedUserData {
|
|
||||||
return cachedData.withUpdatedWallpaper(previousWallpaper)
|
|
||||||
} else {
|
|
||||||
return cachedData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class PendingPeerMediaUploadContext {
|
private final class PendingPeerMediaUploadContext {
|
||||||
var value: PeerMediaUploadingItem
|
var value: PeerMediaUploadingItem
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
@ -225,98 +180,93 @@ private final class PendingPeerMediaUploadManagerImpl {
|
|||||||
let accountPeerId = self.accountPeerId
|
let accountPeerId = self.accountPeerId
|
||||||
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
let context = PendingPeerMediaUploadContext(value: PeerMediaUploadingItem(content: content, messageId: nil, previousState: nil, progress: 0.0))
|
let context = PendingPeerMediaUploadContext(value: PeerMediaUploadingItem(content: content, messageId: nil, progress: 0.0))
|
||||||
self.contexts[peerId] = context
|
self.contexts[peerId] = context
|
||||||
|
|
||||||
context.disposable.set(
|
context.disposable.set(
|
||||||
(self.postbox.transaction({ transaction -> (EngineMessage.Id, PeerMediaUploadingItem.PreviousState?)? in
|
(self.postbox.transaction({ transaction -> EngineMessage.Id? in
|
||||||
let storeMessage = generatePeerMediaMessage(network: network, accountPeerId: accountPeerId, transaction: transaction, peerId: peerId, content: content)
|
let storeMessage = generatePeerMediaMessage(network: network, accountPeerId: accountPeerId, transaction: transaction, peerId: peerId, content: content)
|
||||||
let globallyUniqueIdToMessageId = transaction.addMessages([storeMessage], location: .Random)
|
let globallyUniqueIdToMessageId = transaction.addMessages([storeMessage], location: .Random)
|
||||||
guard let globallyUniqueId = storeMessage.globallyUniqueId, let messageId = globallyUniqueIdToMessageId[globallyUniqueId] else {
|
guard let globallyUniqueId = storeMessage.globallyUniqueId, let messageId = globallyUniqueIdToMessageId[globallyUniqueId] else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let previousState = preparePeerMediaUpload(transaction: transaction, peerId: peerId, content: content)
|
return messageId
|
||||||
return (messageId, previousState)
|
|
||||||
})
|
})
|
||||||
|> deliverOn(queue)).start(next: { [weak self, weak context] messageIdAndPreviousState in
|
|> deliverOn(queue)).start(next: { [weak self, weak context] messageId in
|
||||||
guard let strongSelf = self, let initialContext = context else {
|
guard let strongSelf = self, let initialContext = context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let context = strongSelf.contexts[peerId], context === initialContext {
|
if let context = strongSelf.contexts[peerId], context === initialContext {
|
||||||
guard let (messageId, previousState) = messageIdAndPreviousState else {
|
guard let messageId = messageId else {
|
||||||
strongSelf.contexts.removeValue(forKey: peerId)
|
strongSelf.contexts.removeValue(forKey: peerId)
|
||||||
context.disposable.dispose()
|
context.disposable.dispose()
|
||||||
strongSelf.updateValues()
|
strongSelf.updateValues()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
context.value = context.value.withMessageId(messageId).withPreviousState(previousState)
|
context.value = context.value.withMessageId(messageId)
|
||||||
strongSelf.updateValues()
|
strongSelf.updateValues()
|
||||||
|
|
||||||
context.disposable.set((uploadPeerMedia(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, content: content)
|
context.disposable.set((uploadPeerMedia(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, content: content)
|
||||||
|> deliverOn(queue)).start(next: { [weak self, weak context] value in
|
|> deliverOn(queue)).start(next: { [weak self, weak context] value in
|
||||||
queue.async {
|
guard let strongSelf = self, let initialContext = context else {
|
||||||
guard let strongSelf = self, let initialContext = context else {
|
return
|
||||||
return
|
}
|
||||||
}
|
if let context = strongSelf.contexts[peerId], context === initialContext {
|
||||||
if let context = strongSelf.contexts[peerId], context === initialContext {
|
switch value {
|
||||||
switch value {
|
case let .done(result):
|
||||||
case let .done(result):
|
context.disposable.set(
|
||||||
context.disposable.set(
|
(postbox.transaction({ transaction -> Message? in
|
||||||
(postbox.transaction({ transaction -> Message? in
|
return transaction.getMessage(messageId)
|
||||||
return transaction.getMessage(messageId)
|
})
|
||||||
})
|
|> deliverOn(queue)
|
||||||
|> deliverOn(queue)
|
).start(next: { [weak self, weak context] message in
|
||||||
).start(next: { [weak self, weak context] message in
|
guard let strongSelf = self, let initialContext = context else {
|
||||||
guard let strongSelf = self, let initialContext = context else {
|
return
|
||||||
|
}
|
||||||
|
if let context = strongSelf.contexts[peerId], context === initialContext {
|
||||||
|
guard let message = message else {
|
||||||
|
strongSelf.contexts.removeValue(forKey: peerId)
|
||||||
|
context.disposable.dispose()
|
||||||
|
strongSelf.updateValues()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let context = strongSelf.contexts[peerId], context === initialContext {
|
context.disposable.set(
|
||||||
guard let message = message else {
|
(applyUpdateMessage(
|
||||||
strongSelf.contexts.removeValue(forKey: peerId)
|
postbox: postbox,
|
||||||
context.disposable.dispose()
|
stateManager: stateManager,
|
||||||
strongSelf.updateValues()
|
message: message,
|
||||||
return
|
cacheReferenceKey: nil,
|
||||||
}
|
result: result,
|
||||||
context.disposable.set(
|
accountPeerId: accountPeerId
|
||||||
(applyUpdateMessage(
|
|
||||||
postbox: postbox,
|
|
||||||
stateManager: stateManager,
|
|
||||||
message: message,
|
|
||||||
cacheReferenceKey: nil,
|
|
||||||
result: result,
|
|
||||||
accountPeerId: accountPeerId
|
|
||||||
)
|
|
||||||
|> deliverOn(queue)).start(completed: { [weak self, weak context] in
|
|
||||||
guard let strongSelf = self, let initialContext = context else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if let context = strongSelf.contexts[peerId], context === initialContext {
|
|
||||||
strongSelf.contexts.removeValue(forKey: peerId)
|
|
||||||
context.disposable.dispose()
|
|
||||||
strongSelf.updateValues()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
|> deliverOn(queue)).start(completed: { [weak self, weak context] in
|
||||||
})
|
guard let strongSelf = self, let initialContext = context else {
|
||||||
)
|
return
|
||||||
strongSelf.updateValues()
|
}
|
||||||
case let .progress(progress):
|
if let context = strongSelf.contexts[peerId], context === initialContext {
|
||||||
context.value = context.value.withProgress(progress)
|
strongSelf.contexts.removeValue(forKey: peerId)
|
||||||
strongSelf.updateValues()
|
context.disposable.dispose()
|
||||||
}
|
strongSelf.updateValues()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
strongSelf.updateValues()
|
||||||
|
case let .progress(progress):
|
||||||
|
context.value = context.value.withProgress(progress)
|
||||||
|
strongSelf.updateValues()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, error: { [weak self, weak context] error in
|
}, error: { [weak self, weak context] error in
|
||||||
queue.async {
|
guard let strongSelf = self, let initialContext = context else {
|
||||||
guard let strongSelf = self, let initialContext = context else {
|
return
|
||||||
return
|
}
|
||||||
}
|
if let context = strongSelf.contexts[peerId], context === initialContext {
|
||||||
if let context = strongSelf.contexts[peerId], context === initialContext {
|
strongSelf.contexts.removeValue(forKey: peerId)
|
||||||
strongSelf.contexts.removeValue(forKey: peerId)
|
context.disposable.dispose()
|
||||||
context.disposable.dispose()
|
strongSelf.updateValues()
|
||||||
strongSelf.updateValues()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -330,7 +280,6 @@ private final class PendingPeerMediaUploadManagerImpl {
|
|||||||
|
|
||||||
if let messageId = context.value.messageId {
|
if let messageId = context.value.messageId {
|
||||||
context.disposable.set(self.postbox.transaction({ transaction in
|
context.disposable.set(self.postbox.transaction({ transaction in
|
||||||
cancelPeerMediaUpload(transaction: transaction, peerId: peerId, previousState: context.value.previousState)
|
|
||||||
transaction.deleteMessages([messageId], forEachMedia: nil)
|
transaction.deleteMessages([messageId], forEachMedia: nil)
|
||||||
}).start())
|
}).start())
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,6 +125,14 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return postbox.transaction { transaction -> Signal<Api.Updates, NoError> in
|
return postbox.transaction { transaction -> Signal<Api.Updates, NoError> in
|
||||||
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
|
if let current = current as? CachedUserData {
|
||||||
|
return current.withUpdatedWallpaper(wallpaper)
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
var inputWallpaper: Api.InputWallPaper?
|
var inputWallpaper: Api.InputWallPaper?
|
||||||
var inputSettings: Api.WallPaperSettings?
|
var inputSettings: Api.WallPaperSettings?
|
||||||
@ -139,19 +147,10 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|> mapToSignal { updates -> Signal<Api.Updates, NoError> in
|
|> mapToSignal { updates -> Signal<Api.Updates, NoError> in
|
||||||
return postbox.transaction { transaction -> Api.Updates in
|
if applyUpdates {
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
stateManager.addUpdates(updates)
|
||||||
if let current = current as? CachedUserData {
|
|
||||||
return current.withUpdatedWallpaper(wallpaper)
|
|
||||||
} else {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if applyUpdates {
|
|
||||||
stateManager.addUpdates(updates)
|
|
||||||
}
|
|
||||||
return updates
|
|
||||||
}
|
}
|
||||||
|
return .single(updates)
|
||||||
}
|
}
|
||||||
} |> switchToLatest
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
|
@ -1071,7 +1071,13 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cancelButtonNode.theme = presentationData.theme
|
if let animatingCrossFade = self.animatingCrossFade {
|
||||||
|
Queue.mainQueue().after(!animatingCrossFade ? ChatThemeScreen.themeCrossfadeDelay * UIView.animationDurationFactor() : 0.0, {
|
||||||
|
self.cancelButtonNode.setTheme(presentationData.theme, animated: true)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.cancelButtonNode.setTheme(presentationData.theme, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
let previousIconColors = iconColors(theme: previousTheme)
|
let previousIconColors = iconColors(theme: previousTheme)
|
||||||
let newIconColors = iconColors(theme: self.presentationData.theme)
|
let newIconColors = iconColors(theme: self.presentationData.theme)
|
||||||
@ -1164,6 +1170,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var animatingCrossFade: Bool?
|
||||||
private func animateCrossfade(animateIcon: Bool) {
|
private func animateCrossfade(animateIcon: Bool) {
|
||||||
if animateIcon, let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) {
|
if animateIcon, let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) {
|
||||||
snapshotView.frame = self.animationNode.frame
|
snapshotView.frame = self.animationNode.frame
|
||||||
@ -1174,6 +1181,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.animatingCrossFade = animateIcon
|
||||||
Queue.mainQueue().after(ChatThemeScreen.themeCrossfadeDelay * UIView.animationDurationFactor()) {
|
Queue.mainQueue().after(ChatThemeScreen.themeCrossfadeDelay * UIView.animationDurationFactor()) {
|
||||||
if let effectView = self.effectNode.view as? UIVisualEffectView {
|
if let effectView = self.effectNode.view as? UIVisualEffectView {
|
||||||
UIView.animate(withDuration: ChatThemeScreen.themeCrossfadeDuration, delay: 0.0, options: .curveLinear) {
|
UIView.animate(withDuration: ChatThemeScreen.themeCrossfadeDuration, delay: 0.0, options: .curveLinear) {
|
||||||
@ -1185,6 +1193,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
let previousColor = self.contentBackgroundNode.backgroundColor ?? .clear
|
let previousColor = self.contentBackgroundNode.backgroundColor ?? .clear
|
||||||
self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
||||||
self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: ChatThemeScreen.themeCrossfadeDuration)
|
self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: ChatThemeScreen.themeCrossfadeDuration)
|
||||||
|
|
||||||
|
self.animatingCrossFade = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) {
|
if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
@ -917,8 +917,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if let intensity, intensity < 100 {
|
if let intensity, intensity > 0 {
|
||||||
dimAlpha = 1.0 - max(0.0, min(1.0, Float(intensity) / 100.0))
|
dimAlpha = max(0.0, min(1.0, Float(intensity) / 100.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.dimLayer.opacity = dimAlpha
|
self.dimLayer.opacity = dimAlpha
|
||||||
|
@ -38,15 +38,20 @@ public class WebAppCancelButtonNode: ASDisplayNode {
|
|||||||
|
|
||||||
public var state: State = .cancel
|
public var state: State = .cancel
|
||||||
|
|
||||||
|
private var _theme: PresentationTheme
|
||||||
public var theme: PresentationTheme {
|
public var theme: PresentationTheme {
|
||||||
didSet {
|
get {
|
||||||
self.setState(self.state, animated: false, force: true)
|
return self._theme
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self._theme = newValue
|
||||||
|
self.setState(self.state, animated: false, animateScale: false, force: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
|
|
||||||
public init(theme: PresentationTheme, strings: PresentationStrings) {
|
public init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
self.theme = theme
|
self._theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
self.buttonNode = HighlightTrackingButtonNode()
|
self.buttonNode = HighlightTrackingButtonNode()
|
||||||
@ -55,6 +60,7 @@ public class WebAppCancelButtonNode: ASDisplayNode {
|
|||||||
self.arrowNode.displaysAsynchronously = false
|
self.arrowNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.labelNode = ImmediateTextNode()
|
self.labelNode = ImmediateTextNode()
|
||||||
|
self.labelNode.displaysAsynchronously = false
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -82,23 +88,40 @@ public class WebAppCancelButtonNode: ASDisplayNode {
|
|||||||
self.setState(.cancel, animated: false, force: true)
|
self.setState(.cancel, animated: false, force: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setState(_ state: State, animated: Bool, force: Bool = false) {
|
public func setTheme(_ theme: PresentationTheme, animated: Bool) {
|
||||||
|
self._theme = theme
|
||||||
|
var animated = animated
|
||||||
|
if self.animatingStateChange {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
|
self.setState(self.state, animated: animated, animateScale: false, force: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var animatingStateChange = false
|
||||||
|
public func setState(_ state: State, animated: Bool, animateScale: Bool = true, force: Bool = false) {
|
||||||
guard self.state != state || force else {
|
guard self.state != state || force else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
if animated, let snapshotView = self.buttonNode.view.snapshotContentTree() {
|
if animated, let snapshotView = self.buttonNode.view.snapshotContentTree() {
|
||||||
|
self.animatingStateChange = true
|
||||||
snapshotView.layer.sublayerTransform = self.buttonNode.subnodeTransform
|
snapshotView.layer.sublayerTransform = self.buttonNode.subnodeTransform
|
||||||
self.view.addSubview(snapshotView)
|
self.view.addSubview(snapshotView)
|
||||||
|
|
||||||
snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.25, removeOnCompletion: false)
|
let duration: Double = animateScale ? 0.25 : 0.3
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
if animateScale {
|
||||||
|
snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.25, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
|
self.animatingStateChange = false
|
||||||
})
|
})
|
||||||
|
|
||||||
self.buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
if animateScale {
|
||||||
self.buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.25)
|
self.buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
self.buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.arrowNode.isHidden = state == .cancel
|
self.arrowNode.isHidden = state == .cancel
|
||||||
|
Loading…
x
Reference in New Issue
Block a user