From 256439b1382a64f4afdea939aad93fefc6c0f552 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Apr 2023 12:41:07 +0400 Subject: [PATCH 1/4] Chat wallpaper fixes --- .../Themes/CustomWallpaperPicker.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 2 +- .../TelegramEngine/Themes/ChatThemes.swift | 23 ++++++----- .../TelegramUI/Sources/ChatThemeScreen.swift | 12 +++++- .../Sources/WallpaperBackgroundNode.swift | 4 +- .../WebUI/Sources/WebAppController.swift | 39 +++++++++++++++---- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index c5d5b92ee7..4ce31af850 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -303,7 +303,7 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa var intensity: Int32? 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) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 6de92eefa2..9c1b0cc225 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -313,7 +313,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } switch entry { case .asset, .contextResult: - return self.sliderNode.value + return 1.0 - self.sliderNode.value default: return nil } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift index 406394d937..8e97221e71 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift @@ -125,6 +125,14 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager return .complete() } return postbox.transaction { transaction -> Signal 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 inputWallpaper: Api.InputWallPaper? var inputSettings: Api.WallPaperSettings? @@ -139,19 +147,10 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager return .complete() } |> mapToSignal { updates -> Signal in - return postbox.transaction { transaction -> Api.Updates in - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in - if let current = current as? CachedUserData { - return current.withUpdatedWallpaper(wallpaper) - } else { - return current - } - }) - if applyUpdates { - stateManager.addUpdates(updates) - } - return updates + if applyUpdates { + stateManager.addUpdates(updates) } + return .single(updates) } } |> switchToLatest } diff --git a/submodules/TelegramUI/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Sources/ChatThemeScreen.swift index 1ca1e7719e..bb157f8e4a 100644 --- a/submodules/TelegramUI/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatThemeScreen.swift @@ -1071,7 +1071,13 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega 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 newIconColors = iconColors(theme: self.presentationData.theme) @@ -1164,6 +1170,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } } + private var animatingCrossFade: Bool? private func animateCrossfade(animateIcon: Bool) { if animateIcon, let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) { snapshotView.frame = self.animationNode.frame @@ -1174,6 +1181,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega }) } + self.animatingCrossFade = animateIcon Queue.mainQueue().after(ChatThemeScreen.themeCrossfadeDelay * UIView.animationDurationFactor()) { if let effectView = self.effectNode.view as? UIVisualEffectView { 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 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.animatingCrossFade = nil } if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) { diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 7076506512..957db88bfb 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -917,8 +917,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode default: break } - if let intensity, intensity < 100 { - dimAlpha = 1.0 - max(0.0, min(1.0, Float(intensity) / 100.0)) + if let intensity, intensity > 0 { + dimAlpha = max(0.0, min(1.0, Float(intensity) / 100.0)) } } self.dimLayer.opacity = dimAlpha diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index ca54875593..f6b4df4f4c 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -38,15 +38,20 @@ public class WebAppCancelButtonNode: ASDisplayNode { public var state: State = .cancel + private var _theme: PresentationTheme public var theme: PresentationTheme { - didSet { - self.setState(self.state, animated: false, force: true) + get { + return self._theme + } + set { + self._theme = newValue + self.setState(self.state, animated: false, animateScale: false, force: true) } } private let strings: PresentationStrings public init(theme: PresentationTheme, strings: PresentationStrings) { - self.theme = theme + self._theme = theme self.strings = strings self.buttonNode = HighlightTrackingButtonNode() @@ -55,6 +60,7 @@ public class WebAppCancelButtonNode: ASDisplayNode { self.arrowNode.displaysAsynchronously = false self.labelNode = ImmediateTextNode() + self.labelNode.displaysAsynchronously = false super.init() @@ -82,23 +88,40 @@ public class WebAppCancelButtonNode: ASDisplayNode { 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 { return } self.state = state if animated, let snapshotView = self.buttonNode.view.snapshotContentTree() { + self.animatingStateChange = true snapshotView.layer.sublayerTransform = self.buttonNode.subnodeTransform self.view.addSubview(snapshotView) - snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.25, removeOnCompletion: false) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in + let duration: Double = animateScale ? 0.25 : 0.3 + 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() + self.animatingStateChange = false }) - self.buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - self.buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.25) + if animateScale { + 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 From 4e72188755db43077c333f069bd6219979d148f2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Apr 2023 19:17:42 +0400 Subject: [PATCH 2/4] Chat wallpaper improvements --- .../TGPhotoEditorController.h | 2 + .../LegacyComponents/TGPhotoEditorUtils.h | 1 + .../LegacyComponents/TGPhotoVideoEditor.h | 2 +- .../Sources/TGCameraCapturedPhoto.m | 3 - .../Sources/TGPhotoEditorController.m | 39 +++- .../Sources/TGPhotoEditorTabController.m | 38 +++- .../Sources/TGPhotoEditorUtils.m | 3 +- .../Sources/TGPhotoToolsController.m | 42 +++-- .../Sources/TGPhotoVideoEditor.m | 63 ++++--- .../Sources/LegacyAttachmentMenu.swift | 23 ++- .../Themes/CustomWallpaperPicker.swift | 74 ++++++++ .../Sources/Themes/WallpaperCropNode.swift | 2 +- .../Themes/WallpaperGalleryController.swift | 55 ++++-- .../Sources/Themes/WallpaperGalleryItem.swift | 161 +++++++++------- .../PendingPeerMediaUploadManager.swift | 175 +++++++----------- 15 files changed, 429 insertions(+), 254 deletions(-) diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h index a1ac3a4f8b..7e480079f4 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h @@ -68,6 +68,8 @@ typedef enum { @property (nonatomic, strong) UIView *entitiesView; +@property (nonatomic, assign) bool ignoreCropForResult; + - (instancetype)initWithContext:(id)context item:(id)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id)adjustments caption:(NSAttributedString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab; - (void)dismissEditor; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h index 948ccab2ff..86a85c0067 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h @@ -46,6 +46,7 @@ CGSize TGPhotoThumbnailSizeForCurrentScreen(); CGSize TGPhotoEditorScreenImageMaxSize(); extern const CGSize TGPhotoEditorResultImageMaxSize; +extern const CGSize TGPhotoEditorResultImageWallpaperMaxSize; #ifdef __cplusplus } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h index e58613cae3..6e5c165ac0 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h @@ -6,6 +6,6 @@ + (void)presentWithContext:(id)context controller:(TGViewController *)controller caption:(NSAttributedString *)caption withItem:(id)item paint:(bool)paint adjustments:(bool)adjustments recipientName:(NSString *)recipientName stickersContext:(id)stickersContext fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed; -+ (void)presentEditorWithContext:(id)context controller:(TGViewController *)controller withItem:(id)item fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots completion:(void (^)(id, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed; ++ (void)presentEditorWithContext:(id)context controller:(TGViewController *)controller withItem:(id)item cropRect:(CGRect)cropRect adjustments:(id)adjustments referenceView:(UIView *)referenceView completion:(void (^)(UIImage *, id))completion fullSizeCompletion:(void (^)(UIImage *))fullSizeCompletion beginTransitionOut:(void (^)())beginTransitionOut finishTransitionOut:(void (^)())finishTransitionOut; @end diff --git a/submodules/LegacyComponents/Sources/TGCameraCapturedPhoto.m b/submodules/LegacyComponents/Sources/TGCameraCapturedPhoto.m index 445a794977..dc6440e395 100644 --- a/submodules/LegacyComponents/Sources/TGCameraCapturedPhoto.m +++ b/submodules/LegacyComponents/Sources/TGCameraCapturedPhoto.m @@ -81,9 +81,6 @@ [[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 { if (image == nil) diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m index b9b5dc374d..7b296a5ff6 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m @@ -982,14 +982,23 @@ bool hasImageAdjustments = editorValues.toolsApplied || saveOnly; bool hasPainting = editorValues.hasPainting; bool hasAnimation = editorValues.paintingData.hasAnimation; + bool ignoreCropForResult = self.ignoreCropForResult; SSignal *(^imageCropSignal)(UIImage *, bool) = ^(UIImage *image, bool resize) { return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { - 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]; + if (ignoreCropForResult) { + if (image.size.width > TGPhotoEditorResultImageWallpaperMaxSize.width || image.size.height > TGPhotoEditorResultImageWallpaperMaxSize.width) { + [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]; return nil; @@ -1045,12 +1054,18 @@ void (^didFinishRenderingFullSizeImage)(UIImage *) = self.didFinishRenderingFullSizeImage; void (^didFinishEditing)(id, UIImage *, UIImage *, bool , void(^)(void)) = self.didFinishEditing; + TGPhotoEditorControllerIntent intent = _intent; [[[[renderedImageSignal map:^id(UIImage *image) { if (!hasImageAdjustments) { - if (hasPainting && !hasAnimation && didFinishRenderingFullSizeImage != nil) - didFinishRenderingFullSizeImage(image); + if (didFinishRenderingFullSizeImage != nil) { + if (hasPainting && !hasAnimation) { + didFinishRenderingFullSizeImage(image); + } else if (intent == TGPhotoEditorControllerWallpaperIntent) { + didFinishRenderingFullSizeImage(nil); + } + } return image; } @@ -1155,6 +1170,13 @@ _portraitToolbarView.alpha = 1.0f; _landscapeToolbarView.alpha = 1.0f; } 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 @@ -1170,6 +1192,13 @@ _landscapeToolbarView.alpha = 0.0f; }]; + if (_intent == TGPhotoEditorControllerWallpaperIntent) { + [UIView animateWithDuration:0.1f animations:^ + { + _backgroundView.alpha = 0.0f; + }]; + } + _currentTabController.beginTransitionOut = self.beginTransitionOut; [self setToolbarHidden:false animated:true]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m index 81a052f13d..9410ea9a1c 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m @@ -28,6 +28,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; UIView *_transitionInReferenceView; UIView *_transitionInParentView; CGRect _transitionTargetFrame; + + UIView *_upperTransitionView; + CGRect _upperTransitionTargetFrame; } @end @@ -114,6 +117,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; _dismissing = false; CGRect targetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame]; + if (self.intent == TGPhotoEditorControllerWallpaperIntent) { + targetFrame = [self.view convertRect:targetFrame toView: parentView]; + } if (_CGRectEqualToRectWithEpsilon(targetFrame, referenceFrame, FLT_EPSILON)) { @@ -157,12 +163,20 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; _transitionView = referenceView; } 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.frame = referenceFrame; - _transitionTargetFrame = [self _targetFrameForTransitionInFromFrame:referenceFrame]; + _transitionTargetFrame = targetFrame; [transitionViewSuperview addSubview:_transitionView]; } @@ -174,6 +188,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; _transitionInProgress = true; CGAffineTransform initialTransform = _transitionView.transform; + [UIView animateWithDuration:0.25 animations:^{ + _upperTransitionView.alpha = 1.0; + }]; [UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^ { if (_animateScale) { @@ -182,6 +199,7 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; _transitionView.transform = CGAffineTransformScale(initialTransform, scale, scale); } else { _transitionView.frame = _transitionTargetFrame; + _upperTransitionView.frame = _upperTransitionTargetFrame; } } completion:^(BOOL finished) { _transitionInProgress = false; @@ -201,6 +219,8 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; } [self _finishedTransitionInWithView:transitionView]; + [_upperTransitionView removeFromSuperview]; + _upperTransitionView = nil; }]; } @@ -275,7 +295,11 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; 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) completion(); @@ -316,6 +340,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; orientation = UIInterfaceOrientationPortrait; CGRect sourceFrame = [self transitionOutSourceFrameForReferenceFrame:referenceView.frame orientation:orientation]; + if (self.intent == TGPhotoEditorControllerWallpaperIntent) { + sourceFrame = [self.view convertRect:sourceFrame toView: parentView]; + } CGRect targetFrame = referenceFrame; toTransitionView.frame = sourceFrame; @@ -334,7 +361,12 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; }; [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); }]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m index 6212c9d14e..949bb0b510 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m @@ -6,7 +6,8 @@ #import #import -const CGSize TGPhotoEditorResultImageMaxSize = { 2560, 2560 }; +const CGSize TGPhotoEditorResultImageMaxSize = { 1280, 1280 }; +const CGSize TGPhotoEditorResultImageWallpaperMaxSize = { 2048, 2048 }; const CGSize TGPhotoEditorScreenImageHardLimitSize = { 1280, 1280 }; const CGSize TGPhotoEditorScreenImageHardLimitLegacySize = { 750, 750 }; diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m index abede25b26..1dfec01a9e 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m @@ -454,24 +454,40 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize UIView *snapshotView = nil; POPSpringAnimation *snapshotAnimation = nil; - if (saving && CGRectIsNull(targetFrame) && parentView != nil) + if (saving && parentView != nil) { - 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); - - [parentView addSubview:snapshotView]; - - snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame]; - snapshotAnimation.fromValue = [NSValue valueWithCGRect:snapshotView.frame]; - snapshotAnimation.toValue = [NSValue valueWithCGRect:targetFrame]; + if (CGRectIsNull(targetFrame)) { + 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); + + [parentView addSubview:snapshotView]; + + snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame]; + 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]; previewAnimation.fromValue = [NSValue valueWithCGRect:previewView.frame]; - previewAnimation.toValue = [NSValue valueWithCGRect:targetFrame]; + previewAnimation.toValue = [NSValue valueWithCGRect:previewTargetFrame]; POPSpringAnimation *previewAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha]; previewAlphaAnimation.fromValue = @(previewView.alpha); diff --git a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m index 60b958e226..537beec6c3 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m +++ b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m @@ -283,7 +283,7 @@ } } -+ (void)presentEditorWithContext:(id)context controller:(TGViewController *)controller withItem:(id)item fromRect:(CGRect)fromRect mainSnapshot:(UIView *)mainSnapshot snapshots:(NSArray *)snapshots completion:(void (^)(id, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed ++ (void)presentEditorWithContext:(id)context controller:(TGViewController *)controller withItem:(id)item cropRect:(CGRect)cropRect adjustments:(id)adjustments referenceView:(UIView *)referenceView completion:(void (^)(UIImage *, id))completion fullSizeCompletion:(void (^)(UIImage *))fullSizeCompletion beginTransitionOut:(void (^)())beginTransitionOut finishTransitionOut:(void (^)())finishTransitionOut; { id windowManager = [context makeOverlayWindowManager]; @@ -291,40 +291,67 @@ 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.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; - - UIImageView *imageView = [[UIImageView alloc] initWithFrame:fromRect]; + *parentView = referenceView.superview; + //UIImageView *imageView = [[UIImageView alloc] initWithFrame:fromRect]; //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; if (referenceFrame != NULL) { startFrame = *referenceFrame; *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) { - + fullSizeCompletion(resultImage); }; - __weak TGPhotoEditorController *weakController = editorController; editorController.didFinishEditing = ^(id adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges, void(^commit)(void)) { if (!hasChanges) @@ -334,6 +361,8 @@ if (strongController == nil) return; + completion(resultImage, adjustments); + }; editorController.requestThumbnailImage = ^(id editableItem) { @@ -347,17 +376,7 @@ editorController.requestOriginalFullSizeImage = ^(id editableItem, NSTimeInterval position) { - if (editableItem.isVideo) { - if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true]; - } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { - return ((TGCameraCapturedVideo *)editableItem).avAsset; - } else { - return [editableItem originalImageSignal:position]; - } - } else { - return [editableItem originalImageSignal:position]; - } + return [editableItem originalImageSignal:position]; }; TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:editorController]; diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index 2adce3e163..9c5a507cd7 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -63,9 +63,8 @@ public enum LegacyMediaEditorMode { 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 legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) legacyController.blocksBackgroundWhenInOverlay = true @@ -85,17 +84,17 @@ public func legacyWallpaperEditor(context: AccountContext, image: UIImage, fromR present(legacyController, nil) - TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, withItem: item, from: fromRect, mainSnapshot: mainSnapshot, snapshots: snapshots as [Any], completion: { item, editingContext in - let adjustments = editingContext?.adjustments(for: item) - if let imageSignal = editingContext?.fullSizeImageUrl(for: item) { - imageSignal.start(next: { value in - if let value = value as? NSURL, let data = try? Data(contentsOf: value as URL), let image = UIImage(data: data) { - completion(image, adjustments) - } - }, error: { _ in }, completed: {}) + TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, with: item, cropRect: cropRect, adjustments: adjustments, referenceView: referenceView, completion: { image, adjustments in + completion(image, adjustments) + }, fullSizeCompletion: { image in + Queue.mainQueue().async { + fullSizeCompletion(image) } - }, dismissed: { [weak legacyController] in + }, beginTransitionOut: { + beginTransitionOut?() + }, finishTransitionOut: { [weak legacyController] in legacyController?.dismiss() + finishTransitionOut?() }) } diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index 4ce31af850..d938dd0358 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -318,3 +318,77 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa return croppedImage }).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 { + return fetchPhotoLibraryImage(localIdentifier: self.asset.localIdentifier, thumbnail: false) + |> filter { value in + return !(value?.1 ?? true) + } + |> mapToSignal { result -> Signal 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() + }) + }) + } +} diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperCropNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperCropNode.swift index d87c423254..00dbe5b327 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperCropNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperCropNode.swift @@ -4,7 +4,7 @@ import Display import AsyncDisplayKit final class WallpaperCropNode: ASDisplayNode, UIScrollViewDelegate { - private let scrollNode: ASScrollNode + let scrollNode: ASScrollNode private var ignoreZoom = false private var ignoreZoomTransition: ContainedViewLayoutTransition? diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index cfd9702754..5352a07f7a 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -167,10 +167,16 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl } 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.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.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 { return } - var snapshots = snapshots - if let toolbarNode = self.toolbarNode, let snapshotView = toolbarNode.view.snapshotContentTree() { - snapshotView.frame = toolbarNode.view.convert(toolbarNode.view.bounds, to: nil) - snapshots.append(snapshotView) - } - - legacyWallpaperEditor(context: context, image: image, fromRect: fromRect, mainSnapshot: mainSnapshot, snapshots: snapshots, transitionCompletion: { - + let item = LegacyWallpaperItem(asset: asset, screenImage: image, dimensions: CGSize(width: asset.pixelWidth, height: asset.pixelHeight)) + legacyWallpaperEditor(context: context, item: item, cropRect: cropRect, adjustments: adjustments, referenceView: referenceView, beginTransitionOut: { [weak self] in + self?.interaction?.beginTransitionFromEditor() + }, finishTransitionOut: { [weak self] in + self?.interaction?.finishTransitionFromEditor() }, completion: { image, adjustments in apply(image, adjustments) + }, fullSizeCompletion: { image in + fullSizeApply(image) }, present: { [weak self] c, a in if let self { 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] = [] @@ -491,7 +522,7 @@ public class WallpaperGalleryController: ViewController { let entry = strongSelf.entries[centralItemNode.index] 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 } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 9c1b0cc225..3ceb39712c 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -99,6 +99,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let wrapperNode: ASDisplayNode let imageNode: TransformImageNode + private let temporaryImageNode: ASImageNode let nativeNode: WallpaperBackgroundNode let brightnessNode: ASDisplayNode private let statusNode: RadialStatusNode @@ -110,6 +111,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { private let dayNightButtonNode: WallpaperNavigationButtonNode private let editButtonNode: WallpaperNavigationButtonNode + private let buttonsContainerNode: SparseNode private let blurButtonNode: WallpaperOptionButtonNode private let motionButtonNode: WallpaperOptionButtonNode private let patternButtonNode: WallpaperOptionButtonNode @@ -159,6 +161,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.wrapperNode = ASDisplayNode() self.imageNode = TransformImageNode() self.imageNode.contentAnimations = .subsequentUpdates + self.temporaryImageNode = ASImageNode() + self.temporaryImageNode.isUserInteractionEnabled = false self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.cropNode = WallpaperCropNode() 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.isUserInteractionEnabled = false + self.buttonsContainerNode = SparseNode() self.blurButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Blurred, value: .check(false)) self.blurButtonNode.setEnabled(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.addSubnode(self.wrapperNode) + self.addSubnode(self.temporaryImageNode) //self.addSubnode(self.statusNode) self.addSubnode(self.serviceBackgroundNode) self.addSubnode(self.messagesContainerNode) + self.addSubnode(self.buttonsContainerNode) - self.addSubnode(self.blurButtonNode) - self.addSubnode(self.motionButtonNode) - self.addSubnode(self.patternButtonNode) - self.addSubnode(self.colorsButtonNode) - self.addSubnode(self.playButtonNode) - self.addSubnode(self.sliderNode) - self.addSubnode(self.cancelButtonNode) - self.addSubnode(self.shareButtonNode) - self.addSubnode(self.dayNightButtonNode) - self.addSubnode(self.editButtonNode) + self.buttonsContainerNode.addSubnode(self.blurButtonNode) + self.buttonsContainerNode.addSubnode(self.motionButtonNode) + self.buttonsContainerNode.addSubnode(self.patternButtonNode) + self.buttonsContainerNode.addSubnode(self.colorsButtonNode) + self.buttonsContainerNode.addSubnode(self.playButtonNode) + self.buttonsContainerNode.addSubnode(self.sliderNode) + self.buttonsContainerNode.addSubnode(self.cancelButtonNode) + self.buttonsContainerNode.addSubnode(self.shareButtonNode) + self.buttonsContainerNode.addSubnode(self.dayNightButtonNode) + self.buttonsContainerNode.addSubnode(self.editButtonNode) self.imageNode.addSubnode(self.brightnessNode) @@ -295,18 +302,24 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } switch entry { case .asset, .contextResult: - if let editedImage = self.editedImage { - 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 - } + return self.cropNode.cropRect default: 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? { guard let entry = self.entry else { return nil @@ -467,67 +480,62 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } @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 } - let originalImage = self.originalImage ?? image - - var nodesToSnapshot = [ - self.cancelButtonNode, - self.editButtonNode, - self.blurButtonNode, - self.motionButtonNode, - self.serviceBackgroundNode - ] - - if let messageNodes = self.messageNodes { - for node in messageNodes { - nodesToSnapshot.append(node) + guard let cropRect = self.cropRect else { + return + } + self.interaction?.editMedia(asset, originalImage, cropRect, self.currentAdjustments, self.cropNode.view, { [weak self] result, adjustments in + guard let self else { + return } - } - - var snapshots: [UIView] = [] - 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.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) 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)) } }) return context })) + + self.imageNode.imageUpdated = { [weak self] _ in + guard let self else { + return + } + 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() + self?.imageNode.imageUpdated = nil + }) + } + }, { [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? public private(set) var editedImage: UIImage? + public private(set) var editedFullSizeImage: UIImage? private var currentAdjustments: TGMediaEditAdjustments? private func animateIntensityChange(delay: Double) { @@ -557,6 +565,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() { self.dismiss() } @@ -579,11 +610,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { var showPreviewTooltip = false if self.entry != entry || self.arguments.colorPreview != previousArguments.colorPreview { - let previousEntry = self.entry self.entry = entry - if previousEntry != entry { - self.preparePatternEditing() - } self.colorsButtonNode.colors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors @@ -1204,9 +1231,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } } - private func preparePatternEditing() { - } - func setMotionEnabled(_ enabled: Bool, animated: Bool) { if enabled { let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) @@ -1606,6 +1630,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.wrapperNode.bounds = CGRect(origin: CGPoint(), size: layout.size) self.updateWrapperLayout(layout: layout, offset: offset, transition: transition) self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size) + self.buttonsContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size) if self.cropNode.supernode == nil { self.imageNode.frame = self.wrapperNode.bounds diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift index dabe3847df..2e6468064e 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift @@ -13,23 +13,17 @@ public final class PeerMediaUploadingItem: Equatable { case generic } - public enum PreviousState: Equatable { - case wallpaper(TelegramWallpaper?) - } - public enum Content: Equatable { case wallpaper(TelegramWallpaper) } public let content: Content public let messageId: EngineMessage.Id? - public let previousState: PreviousState? 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.messageId = messageId - self.previousState = previousState self.progress = progress } @@ -40,9 +34,6 @@ public final class PeerMediaUploadingItem: Equatable { if lhs.messageId != rhs.messageId { return false } - if lhs.previousState != rhs.previousState { - return false - } if lhs.progress != rhs.progress { return false } @@ -50,15 +41,11 @@ public final class PeerMediaUploadingItem: Equatable { } 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 { - return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, previousState: self.previousState, progress: progress) - } - - func withPreviousState(_ previousState: PreviousState?) -> PeerMediaUploadingItem { - return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, previousState: previousState, progress: self.progress) + return PeerMediaUploadingItem(content: self.content, messageId: self.messageId, progress: 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) } -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 { var value: PeerMediaUploadingItem let disposable = MetaDisposable() @@ -225,98 +180,93 @@ private final class PendingPeerMediaUploadManagerImpl { let accountPeerId = self.accountPeerId 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 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 globallyUniqueIdToMessageId = transaction.addMessages([storeMessage], location: .Random) guard let globallyUniqueId = storeMessage.globallyUniqueId, let messageId = globallyUniqueIdToMessageId[globallyUniqueId] else { return nil } - let previousState = preparePeerMediaUpload(transaction: transaction, peerId: peerId, content: content) - return (messageId, previousState) + return messageId }) - |> 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 { return } if let context = strongSelf.contexts[peerId], context === initialContext { - guard let (messageId, previousState) = messageIdAndPreviousState else { + guard let messageId = messageId else { strongSelf.contexts.removeValue(forKey: peerId) context.disposable.dispose() strongSelf.updateValues() return } - context.value = context.value.withMessageId(messageId).withPreviousState(previousState) + context.value = context.value.withMessageId(messageId) strongSelf.updateValues() context.disposable.set((uploadPeerMedia(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, content: content) |> deliverOn(queue)).start(next: { [weak self, weak context] value in - queue.async { - guard let strongSelf = self, let initialContext = context else { - return - } - if let context = strongSelf.contexts[peerId], context === initialContext { - switch value { - case let .done(result): - context.disposable.set( - (postbox.transaction({ transaction -> Message? in - return transaction.getMessage(messageId) - }) - |> deliverOn(queue) - ).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 { + switch value { + case let .done(result): + context.disposable.set( + (postbox.transaction({ transaction -> Message? in + return transaction.getMessage(messageId) + }) + |> deliverOn(queue) + ).start(next: { [weak self, weak context] message in + 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 } - if let context = strongSelf.contexts[peerId], context === initialContext { - guard let message = message else { - strongSelf.contexts.removeValue(forKey: peerId) - context.disposable.dispose() - strongSelf.updateValues() - return - } - context.disposable.set( - (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() - } - }) + context.disposable.set( + (applyUpdateMessage( + postbox: postbox, + stateManager: stateManager, + message: message, + cacheReferenceKey: nil, + result: result, + accountPeerId: accountPeerId ) - } - }) - ) - strongSelf.updateValues() - case let .progress(progress): - context.value = context.value.withProgress(progress) - strongSelf.updateValues() - } + |> 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() + } + }) + ) + } + }) + ) + strongSelf.updateValues() + case let .progress(progress): + context.value = context.value.withProgress(progress) + strongSelf.updateValues() } } }, error: { [weak self, weak context] error in - queue.async { - 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() - } + 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() } })) } @@ -330,7 +280,6 @@ private final class PendingPeerMediaUploadManagerImpl { if let messageId = context.value.messageId { context.disposable.set(self.postbox.transaction({ transaction in - cancelPeerMediaUpload(transaction: transaction, peerId: peerId, previousState: context.value.previousState) transaction.deleteMessages([messageId], forEachMedia: nil) }).start()) } else { From 61cc0b5ed19bbbe58a0d275d50ea6060073ca845 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Apr 2023 19:30:17 +0400 Subject: [PATCH 3/4] Chat wallpaper improvements --- .../Sources/Themes/WallpaperGalleryItem.swift | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 3ceb39712c..dd37931e63 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -505,17 +505,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode { }) return context })) - - self.imageNode.imageUpdated = { [weak self] _ in - guard let self else { - return - } - 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() - self?.imageNode.imageUpdated = nil - }) - } + + 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() + self?.imageNode.imageUpdated = nil + }) }, { [weak self] image in guard let self else { return From 32251e1ef2d2852be730292229767385bed0b2d7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Apr 2023 19:37:39 +0400 Subject: [PATCH 4/4] Chat wallpaper fixes --- submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index dd37931e63..a05ca062e6 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -509,7 +509,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode { 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() - self?.imageNode.imageUpdated = nil }) }, { [weak self] image in guard let self else {