Merge commit '32251e1ef2d2852be730292229767385bed0b2d7'

This commit is contained in:
Ali 2023-04-12 20:48:31 +04:00
commit 5f1d7970ce
19 changed files with 480 additions and 279 deletions

View File

@ -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;

View File

@ -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
} }

View File

@ -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

View File

@ -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)

View File

@ -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];

View File

@ -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);
}]; }];

View File

@ -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 };

View File

@ -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);

View File

@ -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];

View File

@ -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?()
}) })
} }

View File

@ -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()
})
})
}
}

View File

@ -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?

View File

@ -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
} }

View File

@ -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

View File

@ -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 {

View File

@ -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
} }

View File

@ -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) {

View File

@ -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

View File

@ -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