From 62470a424f6be1d6d86fd838c8d7153c8ae9ed8f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 11 Apr 2023 22:25:41 +0400 Subject: [PATCH] Chat wallpaper improvements --- .../TGMediaPickerGalleryModel.h | 2 +- .../TGModernGalleryController.h | 2 + .../TGPhotoEditorController.h | 3 +- .../LegacyComponents/TGPhotoVideoEditor.h | 4 +- .../LegacyComponents/Sources/PGBlurTool.m | 5 + .../LegacyComponents/Sources/PGPhotoTool.h | 1 + .../LegacyComponents/Sources/PGPhotoTool.m | 5 + .../LegacyComponents/Sources/PGVignetteTool.m | 5 + .../Sources/TGMediaPickerGalleryModel.m | 9 +- .../Sources/TGModernGalleryController.m | 6 + .../Sources/TGPhotoEditorUtils.m | 2 +- .../Sources/TGPhotoToolsController.m | 5 + .../Sources/TGPhotoVideoEditor.m | 220 +++++++++++++++++- .../Sources/LegacyAttachmentMenu.swift | 47 +++- .../Sources/MediaPickerGridItem.swift | 9 +- .../Themes/CustomWallpaperPicker.swift | 18 +- .../ThemeColorsGridControllerNode.swift | 2 +- .../Sources/Themes/ThemeGridController.swift | 8 +- .../Themes/ThemeSettingsController.swift | 13 +- .../Themes/WallpaperGalleryController.swift | 50 +++- .../Sources/Themes/WallpaperGalleryItem.swift | 167 +++++++++++-- .../Themes/WallpaperOptionButtonNode.swift | 25 +- .../TelegramEngine/Themes/ChatThemes.swift | 25 +- submodules/TelegramPresentationData/BUILD | 1 + .../ChatControllerBackgroundNode.swift | 43 +++- .../Favorite.imageset/Contents.json | 12 + .../Media Grid/Favorite.imageset/heart_18.pdf | 75 ++++++ .../Contents.json | 12 + .../WallpaperAdjustments.imageset/edit.pdf | 101 ++++++++ .../TelegramUI/Sources/ChatController.swift | 32 ++- ...ageProfilePhotoSuggestionContentNode.swift | 1 + ...hatMessageWallpaperBubbleContentNode.swift | 70 +++++- .../TelegramUI/Sources/ChatThemeScreen.swift | 129 +++++----- .../Sources/PeerInfo/PeerInfoScreen.swift | 11 +- .../Sources/WallpaperBackgroundNode.swift | 1 + .../Sources/WallpaperResources.swift | 2 +- .../WebUI/Sources/WebAppController.swift | 20 +- 37 files changed, 974 insertions(+), 169 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/heart_18.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/edit.pdf diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h index 8d2175553f..4d4030a97d 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h @@ -49,6 +49,6 @@ - (instancetype)initWithContext:(id)context items:(NSArray *)items focusItem:(id)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName; - (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab; -- (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots; +- (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots fromRect:(CGRect)fromRect; @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryController.h index 533f54948b..c65a9552f9 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryController.h @@ -48,6 +48,8 @@ typedef NS_ENUM(NSUInteger, TGModernGalleryScrollAnimationDirection) { - (void)dismissWhenReady; - (void)dismissWhenReadyAnimated:(bool)animated; +- (void)setScrollViewHidden:(bool)hidden; + - (bool)isFullyOpaque; @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h index e82f3b5c6d..a1ac3a4f8b 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h @@ -23,7 +23,8 @@ typedef enum { TGPhotoEditorControllerVideoIntent = (1 << 4), TGPhotoEditorControllerForumAvatarIntent = (1 << 5), TGPhotoEditorControllerSuggestedAvatarIntent = (1 << 6), - TGPhotoEditorControllerSuggestingAvatarIntent = (1 << 7) + TGPhotoEditorControllerSuggestingAvatarIntent = (1 << 7), + TGPhotoEditorControllerWallpaperIntent = (1 << 8) } TGPhotoEditorControllerIntent; @interface TGPhotoEditorController : TGOverlayController diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h index 737380548d..e58613cae3 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoVideoEditor.h @@ -4,6 +4,8 @@ + (void)presentWithContext:(id)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video stickersContext:(id)stickersContext transitionView:(UIView *)transitionView senderName:(NSString *)senderName didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo dismissed:(void (^)(void))dismissed; -+ (void)presentWithContext:(id)context controller:(TGViewController *)controller caption:(NSAttributedString *)caption withItem:(id)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id)stickersContext snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed; ++ (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; @end diff --git a/submodules/LegacyComponents/Sources/PGBlurTool.m b/submodules/LegacyComponents/Sources/PGBlurTool.m index eedcb75f81..21cd6392b9 100644 --- a/submodules/LegacyComponents/Sources/PGBlurTool.m +++ b/submodules/LegacyComponents/Sources/PGBlurTool.m @@ -218,6 +218,11 @@ return false; } +- (bool)isRegional +{ + return true; +} + - (bool)isAvialableForVideo { return false; diff --git a/submodules/LegacyComponents/Sources/PGPhotoTool.h b/submodules/LegacyComponents/Sources/PGPhotoTool.h index 7e2ec58156..474a17a514 100644 --- a/submodules/LegacyComponents/Sources/PGPhotoTool.h +++ b/submodules/LegacyComponents/Sources/PGPhotoTool.h @@ -35,6 +35,7 @@ typedef enum @property (nonatomic, readonly) NSInteger order; @property (nonatomic, readonly) bool isHidden; +@property (nonatomic, readonly) bool isRegional; @property (nonatomic, readonly) NSString *shaderString; @property (nonatomic, readonly) NSString *ancillaryShaderString; diff --git a/submodules/LegacyComponents/Sources/PGPhotoTool.m b/submodules/LegacyComponents/Sources/PGPhotoTool.m index 1c8a0263be..d8dba44ec7 100644 --- a/submodules/LegacyComponents/Sources/PGPhotoTool.m +++ b/submodules/LegacyComponents/Sources/PGPhotoTool.m @@ -38,6 +38,11 @@ return true; } +- (bool)isRegional +{ + return false; +} + - (bool)isAvialableForVideo { return true; diff --git a/submodules/LegacyComponents/Sources/PGVignetteTool.m b/submodules/LegacyComponents/Sources/PGVignetteTool.m index 3e64b56e5b..dc307df873 100644 --- a/submodules/LegacyComponents/Sources/PGVignetteTool.m +++ b/submodules/LegacyComponents/Sources/PGVignetteTool.m @@ -38,6 +38,11 @@ return (ABS(((NSNumber *)self.displayValue).floatValue - (float)self.defaultValue) < FLT_EPSILON); } +- (bool)isRegional +{ + return true; +} + - (NSArray *)parameters { if (!_parameters) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m index 53ebd775e0..c786c20090 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m @@ -338,10 +338,10 @@ - (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab { - [self presentPhotoEditorForItem:item tab:tab snapshots:@[]]; + [self presentPhotoEditorForItem:item tab:tab snapshots:@[] fromRect:CGRectZero]; } -- (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots +- (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots fromRect:(CGRect)fromRect { __weak TGMediaPickerGalleryModel *weakSelf = self; @@ -356,12 +356,15 @@ CGRect refFrame = CGRectZero; UIView *editorReferenceView = [self referenceViewForItem:item frame:&refFrame]; + if (!CGRectEqualToRect(fromRect, CGRectZero)) { + refFrame = fromRect; + } UIView *referenceView = nil; UIImage *screenImage = nil; UIView *referenceParentView = nil; UIImage *image = nil; - UIView *entitiesView = nil; + UIView *entitiesView = nil; id editableMediaItem = item.editableMediaItem; diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryController.m b/submodules/LegacyComponents/Sources/TGModernGalleryController.m index 6596d5cf3f..e38b44e82d 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryController.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryController.m @@ -166,6 +166,12 @@ static void adjustFrameRate(CAAnimation *animation) { [self dismissWhenReadyAnimated:animated force:false]; } +- (void)setScrollViewHidden:(bool)hidden { + TGDispatchAfter(0.01, dispatch_get_main_queue(), ^{ + _view.scrollView.hidden = hidden; + }); +} + - (void)dismissWhenReadyAnimated:(bool)animated force:(bool)force { if (animated) { diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m index c2dfab89a4..6212c9d14e 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m @@ -6,7 +6,7 @@ #import #import -const CGSize TGPhotoEditorResultImageMaxSize = { 1280, 1280 }; +const CGSize TGPhotoEditorResultImageMaxSize = { 2560, 2560 }; 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 f66777e0ae..abede25b26 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m @@ -150,6 +150,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize } if (!tool.isHidden) { + if (tool.isRegional && self.intent == TGPhotoEditorControllerWallpaperIntent) { + continue; + } [tools addObject:tool]; if (tool.isSimple) [simpleTools addObject:tool]; @@ -1020,6 +1023,8 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize { if (self.photoEditor.forVideo) { return TGPhotoEditorToolsTab | TGPhotoEditorTintTab | TGPhotoEditorCurvesTab; + } else if (self.intent == TGPhotoEditorControllerWallpaperIntent) { + return TGPhotoEditorToolsTab | TGPhotoEditorTintTab | TGPhotoEditorCurvesTab; } else { return TGPhotoEditorToolsTab | TGPhotoEditorTintTab | TGPhotoEditorBlurTab | TGPhotoEditorCurvesTab; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m index 38e1c6ebdc..60b958e226 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m +++ b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m @@ -154,7 +154,7 @@ } } -+ (void)presentWithContext:(id)context controller:(TGViewController *)controller caption:(NSAttributedString *)caption withItem:(id)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id)stickersContext snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed ++ (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 { id windowManager = [context makeOverlayWindowManager]; id windowContext = [windowManager context]; @@ -201,6 +201,11 @@ [editingContext setCaption:caption forItem:editableItem]; }; + model.didFinishRenderingFullSizeImage = ^(id editableItem, UIImage *resultImage) + { + [editingContext setFullSizeImage:resultImage forItem:editableItem]; + }; + model.interfaceView.hasSwipeGesture = false; galleryController.model = model; @@ -255,10 +260,10 @@ } }; - if (paint) { + if (paint || adjustments) { [model.interfaceView immediateEditorTransitionIn]; } - + for (UIView *view in snapshots) { [galleryController.view addSubview:view]; } @@ -269,9 +274,216 @@ if (paint) { TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{ - [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorPaintTab snapshots:snapshots]; + [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorPaintTab snapshots:snapshots fromRect:fromRect]; + }); + } else if (adjustments) { + TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{ + [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorToolsTab snapshots:snapshots fromRect:fromRect]; }); } } ++ (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 +{ + id windowManager = [context makeOverlayWindowManager]; + + TGMediaEditingContext *editingContext = [[TGMediaEditingContext alloc] init]; + + UIImage *thumbnailImage; + + TGPhotoEditorController *editorController = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:item intent:TGPhotoEditorControllerWallpaperIntent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:TGPhotoEditorToolsTab selectedTab:TGPhotoEditorToolsTab]; + editorController.editingContext = editingContext; + editorController.dontHideStatusBar = true; + + editorController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) + { + *referenceFrame = fromRect; + + UIImageView *imageView = [[UIImageView alloc] initWithFrame:fromRect]; + //imageView.image = image; + + return imageView; + }; + + editorController.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) + { + CGRect startFrame = CGRectZero; + if (referenceFrame != NULL) + { + startFrame = *referenceFrame; + *referenceFrame = fromRect; + } + + //[strongSelf transitionBackFromResultControllerWithReferenceFrame:startFrame]; + + return nil; //strongSelf->_previewView; + }; + + editorController.didFinishRenderingFullSizeImage = ^(UIImage *resultImage) + { + + }; + + __weak TGPhotoEditorController *weakController = editorController; + editorController.didFinishEditing = ^(id adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges, void(^commit)(void)) + { + if (!hasChanges) + return; + + __strong TGPhotoEditorController *strongController = weakController; + if (strongController == nil) + return; + + }; + editorController.requestThumbnailImage = ^(id editableItem) + { + return [editableItem thumbnailImageSignal]; + }; + + editorController.requestOriginalScreenSizeImage = ^(id editableItem, NSTimeInterval position) + { + return [editableItem screenImageSignal:position]; + }; + + 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]; + } + }; + + TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:editorController]; + controllerWindow.hidden = false; + controller.view.clipsToBounds = true; + +// TGModernGalleryController *galleryController = [[TGModernGalleryController alloc] initWithContext:windowContext]; +// galleryController.adjustsStatusBarVisibility = true; +// galleryController.animateTransition = false; +// galleryController.finishedTransitionIn = ^(id item, TGModernGalleryItemView *itemView) { +// appeared(); +// }; +// //galleryController.hasFadeOutTransition = true; +// +// id galleryItem = nil; +// if (item.isVideo) +// galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:item]; +// else +// galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:item]; +// galleryItem.editingContext = editingContext; +// galleryItem.stickersContext = stickersContext; +// +// TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:@[galleryItem] focusItem:galleryItem selectionContext:nil editingContext:editingContext hasCaptions:true allowCaptionEntities:true hasTimer:false onlyCrop:false inhibitDocumentCaptions:false hasSelectionPanel:false hasCamera:false recipientName:recipientName]; +// model.controller = galleryController; +// model.stickersContext = stickersContext; +// +// model.willFinishEditingItem = ^(id editableItem, id adjustments, id representation, bool hasChanges) +// { +// if (hasChanges) +// { +// [editingContext setAdjustments:adjustments forItem:editableItem]; +// [editingContext setTemporaryRep:representation forItem:editableItem]; +// } +// }; +// +// model.didFinishEditingItem = ^(id editableItem, __unused id adjustments, UIImage *resultImage, UIImage *thumbnailImage) +// { +// [editingContext setImage:resultImage thumbnailImage:thumbnailImage forItem:editableItem synchronous:false]; +// }; +// +// model.saveItemCaption = ^(id editableItem, NSAttributedString *caption) +// { +// [editingContext setCaption:caption forItem:editableItem]; +// }; +// +// model.didFinishRenderingFullSizeImage = ^(id editableItem, UIImage *resultImage) +// { +// [editingContext setFullSizeImage:resultImage forItem:editableItem]; +// }; +// +// model.interfaceView.hasSwipeGesture = false; +// galleryController.model = model; +// +// __weak TGModernGalleryController *weakGalleryController = galleryController; +// +// [model.interfaceView updateSelectionInterface:1 counterVisible:false animated:false]; +// model.interfaceView.thumbnailSignalForItem = ^SSignal *(id item) +// { +// return nil; +// }; +// model.interfaceView.donePressed = ^(TGMediaPickerGalleryItem *item) +// { +// __strong TGModernGalleryController *strongController = weakGalleryController; +// if (strongController == nil) +// return; +// +// if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]]) +// { +// TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item]; +// [itemView stop]; +// [itemView setPlayButtonHidden:true animated:true]; +// } +// +// if (completion != nil) +// completion(item.asset, editingContext); +// +// [strongController dismissWhenReadyAnimated:true]; +// }; +// +// galleryController.beginTransitionIn = ^UIView *(__unused TGMediaPickerGalleryItem *item, __unused TGModernGalleryItemView *itemView) +// { +// return nil; +// }; +// +// galleryController.beginTransitionOut = ^UIView *(__unused TGMediaPickerGalleryItem *item, __unused TGModernGalleryItemView *itemView) +// { +// return nil; +// }; +// +// galleryController.completedTransitionOut = ^ +// { +// TGModernGalleryController *strongGalleryController = weakGalleryController; +// 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 (dismissed) { +// dismissed(); +// } +// }; +// +// if (paint || adjustments) { +// [model.interfaceView immediateEditorTransitionIn]; +// } +// +// for (UIView *view in snapshots) { +// [galleryController.view addSubview:view]; +// } +// +// TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:galleryController]; +// controllerWindow.hidden = false; +// galleryController.view.clipsToBounds = true; +// +// if (paint) { +// TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{ +// [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorPaintTab snapshots:snapshots fromRect:fromRect]; +// }); +// } else if (adjustments) { +// TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{ +// [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorToolsTab snapshots:snapshots fromRect:fromRect]; +// }); +// } +} + + @end diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index 8ea2a99219..2adce3e163 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -58,7 +58,48 @@ public enum LegacyAttachmentMenuMediaEditing { case file } -public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, present: @escaping (ViewController, Any?) -> Void) { +public enum LegacyMediaEditorMode { + case draw + 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) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) + legacyController.blocksBackgroundWhenInOverlay = true + legacyController.acceptsFocusWhenInOverlay = true + legacyController.statusBar.statusBarStyle = .Ignore + legacyController.controllerLoaded = { [weak legacyController] in + legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true + } + + let emptyController = LegacyEmptyController(context: legacyController.context)! + emptyController.navigationBarShouldBeHidden = true + let navigationController = makeLegacyNavigationController(rootController: emptyController) + navigationController.setNavigationBarHidden(true, animated: false) + legacyController.bind(controller: navigationController) + + legacyController.enableSizeClassSignal = true + + 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: {}) + } + }, dismissed: { [weak legacyController] in + legacyController?.dismiss() + }) +} + +public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, mode: LegacyMediaEditorMode, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, present: @escaping (ViewController, Any?) -> Void) { let _ = (fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media) |> deliverOnMainQueue).start(next: { (value, isImage) in guard case let .data(data) = value, data.complete else { @@ -107,7 +148,7 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: present(legacyController, nil) - TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, withItem: item, paint: true, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: snapshots as [Any], immediate: transitionCompletion != nil, appeared: { + TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, withItem: item, paint: mode == .draw, adjustments: mode == .adjustments, recipientName: recipientName, stickersContext: paintStickersContext, from: .zero, mainSnapshot: nil, snapshots: snapshots as [Any], immediate: transitionCompletion != nil, appeared: { transitionCompletion?() }, completion: { result, editingContext in let nativeGenerator = legacyAssetPickerItemGenerator() @@ -365,7 +406,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitl present(legacyController, nil) - TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: NSAttributedString(), withItem: item, paint: false, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: [], immediate: false, appeared: { + TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: NSAttributedString(), withItem: item, paint: false, adjustments: false, recipientName: recipientName, stickersContext: paintStickersContext, from: .zero, mainSnapshot: nil, snapshots: [], immediate: false, appeared: { }, completion: { result, editingContext in let nativeGenerator = legacyAssetPickerItemGenerator() var selectableResult: TGMediaSelectableItem? diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index dac85ccde4..6d65780560 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -323,7 +323,14 @@ final class MediaPickerGridItemNode: GridItemNode { strongSelf.updateHasSpoiler(hasSpoiler) })) - if asset.mediaType == .video { + if asset.isFavorite { + self.typeIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Favorite"), color: .white) + if self.typeIconNode.supernode == nil { + self.addSubnode(self.gradientNode) + self.addSubnode(self.typeIconNode) + self.setNeedsLayout() + } + } else if asset.mediaType == .video { if asset.mediaSubtypes.contains(.videoHighFrameRate) { self.typeIconNode.image = UIImage(bundleImageName: "Media Editor/MediaSlomo") } else if asset.mediaSubtypes.contains(.videoTimelapse) { diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index 59fd6e67c8..c5d5b92ee7 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -25,9 +25,9 @@ func presentCustomWallpaperPicker(context: AccountContext, present: @escaping (V controller.selectionBlock = { [weak legacyController] asset, _ in if let asset = asset { let controller = WallpaperGalleryController(context: context, source: .asset(asset.backingAsset)) - controller.apply = { [weak legacyController, weak controller] wallpaper, mode, cropRect, brightness in + controller.apply = { [weak legacyController, weak controller] wallpaper, mode, editedImage, cropRect, brightness in if let legacyController = legacyController, let controller = controller { - uploadCustomWallpaper(context: context, wallpaper: wallpaper, mode: mode, cropRect: cropRect, brightness: brightness, completion: { [weak legacyController, weak controller] in + uploadCustomWallpaper(context: context, wallpaper: wallpaper, mode: mode, editedImage: nil, cropRect: cropRect, brightness: brightness, completion: { [weak legacyController, weak controller] in if let legacyController = legacyController, let controller = controller { legacyController.dismiss() controller.dismiss(forceAway: true) @@ -47,8 +47,8 @@ func presentCustomWallpaperPicker(context: AccountContext, present: @escaping (V }) } -func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightness: CGFloat?, completion: @escaping () -> Void) { - let imageSignal: Signal +func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, editedImage: UIImage?, cropRect: CGRect?, brightness: CGFloat?, completion: @escaping () -> Void) { + var imageSignal: Signal switch wallpaper { case let .wallpaper(wallpaper, _): switch wallpaper { @@ -112,6 +112,10 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE } } + if let editedImage { + imageSignal = .single(editedImage) + } + let _ = (imageSignal |> map { image -> UIImage in var croppedImage = UIImage() @@ -196,7 +200,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE }).start() } -public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightness: CGFloat?, peerId: PeerId, completion: @escaping () -> Void) { +public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, editedImage: UIImage?, cropRect: CGRect?, brightness: CGFloat?, peerId: PeerId, completion: @escaping () -> Void) { var imageSignal: Signal switch wallpaper { case let .wallpaper(wallpaper, _): @@ -263,6 +267,10 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa } } + if let editedImage { + imageSignal = .single(editedImage) + } + let _ = (imageSignal |> map { image -> UIImage in var croppedImage = UIImage() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift index 387b773374..64e2c7f57b 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift @@ -158,7 +158,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { } controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, _, _, _ in + controller.apply = { [weak self] wallpaper, _, _, _, _ in if let strongSelf = self, let mode = strongSelf.controller?.mode, case let .peer(peer) = mode, case let .wallpaper(wallpaperValue, _) = wallpaper { let _ = (strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: wallpaperValue) |> deliverOnMainQueue).start(completed: { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift index cef1f6070c..048a0af79e 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift @@ -120,9 +120,9 @@ public final class ThemeGridController: ViewController { self.displayNode = ThemeGridControllerNode(context: self.context, presentationData: self.presentationData, presentPreviewController: { [weak self] source in if let strongSelf = self { let controller = WallpaperGalleryController(context: strongSelf.context, source: source) - controller.apply = { [weak self, weak controller] wallpaper, options, cropRect, brightness in + controller.apply = { [weak self, weak controller] wallpaper, options, editedImage, cropRect, brightness in if let strongSelf = self { - uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, completion: { [weak self, weak controller] in + uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, completion: { [weak self, weak controller] in if let strongSelf = self { strongSelf.deactivateSearch(animated: false) strongSelf.controllerNode.scrollToTop(animated: false) @@ -148,9 +148,9 @@ public final class ThemeGridController: ViewController { return } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset)) - controller.apply = { [weak self, weak controller] wallpaper, options, cropRect, brightness in + controller.apply = { [weak self, weak controller] wallpaper, options, editedImage, cropRect, brightness in if let strongSelf = self, let controller = controller { - uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, completion: { [weak controller] in + uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, completion: { [weak controller] in if let controller = controller { controller.dismiss(forceAway: true) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 0e3fd9f894..7fa3424a02 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -129,7 +129,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case stickersAndEmoji case otherHeader(PresentationTheme, String) case showNextMediaOnTap(PresentationTheme, String, Bool) - case animationsInfo(PresentationTheme, String) + case showNextMediaOnTapInfo(PresentationTheme, String) var section: ItemListSectionId { switch self { @@ -143,7 +143,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ThemeSettingsControllerSection.icon.rawValue case .powerSaving, .stickersAndEmoji: return ThemeSettingsControllerSection.message.rawValue - case .otherHeader, .showNextMediaOnTap, .animationsInfo: + case .otherHeader, .showNextMediaOnTap, .showNextMediaOnTapInfo: return ThemeSettingsControllerSection.other.rawValue } } @@ -180,7 +180,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return 13 case .showNextMediaOnTap: return 14 - case .animationsInfo: + case .showNextMediaOnTapInfo: return 15 } } @@ -277,8 +277,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } else { return false } - case let .animationsInfo(lhsTheme, lhsText): - if case let .animationsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .showNextMediaOnTapInfo(lhsTheme, lhsText): + if case let .showNextMediaOnTapInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false @@ -347,7 +347,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleShowNextMediaOnTap(value) }, tag: ThemeSettingsEntryTag.animations) - case let .animationsInfo(_, text): + case let .showNextMediaOnTapInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) } } @@ -405,6 +405,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased())) entries.append(.showNextMediaOnTap(presentationData.theme, strings.Appearance_ShowNextMediaOnTap, mediaSettings.showNextMediaOnTap)) + entries.append(.showNextMediaOnTapInfo(presentationData.theme, strings.Appearance_ShowNextMediaOnTapInfo)) return entries } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index 3dc081ba42..cfd9702754 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -16,6 +16,8 @@ import GalleryUI import HexColor import CounterContollerTitleView import UndoUI +import LegacyComponents +import LegacyMediaPickerUI public enum WallpaperListType { case wallpapers(WallpaperPresentationOptions?) @@ -164,6 +166,14 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl return .file(TelegramWallpaper.File(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(colors: colorValues, intensity: intensityValue, rotation: rotation))) } +class WallpaperGalleryInteraction { + let editMedia: (UIImage, CGRect, UIView, [UIView], @escaping (UIImage, TGMediaEditAdjustments?) -> Void) -> Void + + init(editMedia: @escaping (UIImage, CGRect, UIView, [UIView], @escaping (UIImage, TGMediaEditAdjustments?) -> Void) -> Void) { + self.editMedia = editMedia + } +} + public class WallpaperGalleryController: ViewController { public enum Mode { case `default` @@ -176,7 +186,9 @@ public class WallpaperGalleryController: ViewController { private let context: AccountContext private let source: WallpaperListSource private let mode: Mode - public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, CGRect?, CGFloat?) -> Void)? + public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, UIImage?, CGRect?, CGFloat?) -> Void)? + + private var interaction: WallpaperGalleryInteraction? private let _ready = Promise() override public var ready: Promise { @@ -230,9 +242,29 @@ 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 + 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: { + + }, completion: { image, adjustments in + apply(image, adjustments) + }, present: { [weak self] c, a in + if let self { + self.present(c, in: .window(.root)) + } + }) + }) + var entries: [WallpaperGalleryEntry] = [] var centralEntryIndex: Int? - switch source { case let .list(wallpapers, central, type): entries = wallpapers.map { .wallpaper($0, nil) } @@ -350,7 +382,7 @@ public class WallpaperGalleryController: ViewController { var i: Int = 0 var updateItems: [GalleryPagerUpdateItem] = [] for entry in entries { - let item = GalleryPagerUpdateItem(index: i, previousIndex: i, item: WallpaperGalleryItem(context: self.context, index: updateItems.count, entry: entry, arguments: arguments, source: self.source, mode: self.mode)) + let item = GalleryPagerUpdateItem(index: i, previousIndex: i, item: WallpaperGalleryItem(context: self.context, index: updateItems.count, entry: entry, arguments: arguments, source: self.source, mode: self.mode, interaction: self.interaction!)) updateItems.append(item) i += 1 } @@ -361,7 +393,7 @@ public class WallpaperGalleryController: ViewController { var updateItems: [GalleryPagerUpdateItem] = [] for i in 0 ..< self.entries.count { if i == index { - let item = GalleryPagerUpdateItem(index: index, previousIndex: index, item: WallpaperGalleryItem(context: self.context, index: index, entry: entry, arguments: arguments, source: self.source, mode: self.mode)) + let item = GalleryPagerUpdateItem(index: index, previousIndex: index, item: WallpaperGalleryItem(context: self.context, index: index, entry: entry, arguments: arguments, source: self.source, mode: self.mode, interaction: self.interaction!)) updateItems.append(item) } } @@ -459,7 +491,7 @@ public class WallpaperGalleryController: ViewController { let entry = strongSelf.entries[centralItemNode.index] if case .peer = strongSelf.mode { - strongSelf.apply?(entry, options, centralItemNode.cropRect, centralItemNode.brightness) + strongSelf.apply?(entry, options, centralItemNode.editedImage, centralItemNode.cropRect, centralItemNode.brightness) return } @@ -617,7 +649,7 @@ public class WallpaperGalleryController: ViewController { break } - strongSelf.apply?(entry, options, centralItemNode.cropRect, centralItemNode.brightness) + strongSelf.apply?(entry, options, nil, centralItemNode.cropRect, centralItemNode.brightness) } } } @@ -695,7 +727,7 @@ public class WallpaperGalleryController: ViewController { break } } - strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring)) + strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring)) } } @@ -723,7 +755,7 @@ public class WallpaperGalleryController: ViewController { itemNode.updateIsColorsPanelActive(strongSelf.colorsPanelEnabled, animated: true) - strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring)) + strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring)) } } @@ -960,7 +992,7 @@ public class WallpaperGalleryController: ViewController { colors = true } - self.galleryNode.pager.replaceItems(zip(0 ..< self.entries.count, self.entries).map({ WallpaperGalleryItem(context: self.context, index: $0, entry: $1, arguments: WallpaperGalleryItemArguments(isColorsList: colors), source: self.source, mode: self.mode) }), centralItemIndex: self.centralEntryIndex) + self.galleryNode.pager.replaceItems(zip(0 ..< self.entries.count, self.entries).map({ WallpaperGalleryItem(context: self.context, index: $0, entry: $1, arguments: WallpaperGalleryItemArguments(isColorsList: colors), source: self.source, mode: self.mode, interaction: self.interaction!) }), centralItemIndex: self.centralEntryIndex) if let initialOptions = self.initialOptions, let itemNode = self.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode { itemNode.options = initialOptions diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 410e741e34..6de92eefa2 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -46,25 +46,27 @@ class WallpaperGalleryItem: GalleryItem { let arguments: WallpaperGalleryItemArguments let source: WallpaperListSource let mode: WallpaperGalleryController.Mode + let interaction: WallpaperGalleryInteraction - init(context: AccountContext, index: Int, entry: WallpaperGalleryEntry, arguments: WallpaperGalleryItemArguments, source: WallpaperListSource, mode: WallpaperGalleryController.Mode) { + init(context: AccountContext, index: Int, entry: WallpaperGalleryEntry, arguments: WallpaperGalleryItemArguments, source: WallpaperListSource, mode: WallpaperGalleryController.Mode, interaction: WallpaperGalleryInteraction) { self.context = context self.index = index self.entry = entry self.arguments = arguments self.source = source self.mode = mode + self.interaction = interaction } func node(synchronous: Bool) -> GalleryItemNode { let node = WallpaperGalleryItemNode(context: self.context) - node.setEntry(self.entry, arguments: self.arguments, source: self.source, mode: self.mode) + node.setEntry(self.entry, arguments: self.arguments, source: self.source, mode: self.mode, interaction: self.interaction) return node } func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? WallpaperGalleryItemNode { - node.setEntry(self.entry, arguments: self.arguments, source: self.source, mode: self.mode) + node.setEntry(self.entry, arguments: self.arguments, source: self.source, mode: self.mode, interaction: self.interaction) } } @@ -93,6 +95,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { private var colorPreview: Bool = false private var contentSize: CGSize? private var arguments = WallpaperGalleryItemArguments() + private var interaction: WallpaperGalleryInteraction? let wrapperNode: ASDisplayNode let imageNode: TransformImageNode @@ -105,7 +108,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { private let cancelButtonNode: WallpaperNavigationButtonNode private let shareButtonNode: WallpaperNavigationButtonNode private let dayNightButtonNode: WallpaperNavigationButtonNode - + private let editButtonNode: WallpaperNavigationButtonNode + private let blurButtonNode: WallpaperOptionButtonNode private let motionButtonNode: WallpaperOptionButtonNode private let patternButtonNode: WallpaperOptionButtonNode @@ -176,8 +180,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.patternButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Pattern, value: .check(false)) self.patternButtonNode.setEnabled(false) - self.serviceBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.45)) - self.serviceBackgroundNode.isHidden = true + self.serviceBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.35)) var sliderValueChangedImpl: ((CGFloat) -> Void)? self.sliderNode = WallpaperSliderNode(minValue: 0.0, maxValue: 1.0, value: 0.7, valueChanged: { value, _ in @@ -192,6 +195,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.shareButtonNode.enableSaturation = true self.dayNightButtonNode = WallpaperNavigationButtonNode(content: .dayNight(isNight: self.isDarkAppearance), dark: true) self.dayNightButtonNode.enableSaturation = true + self.editButtonNode = WallpaperNavigationButtonNode(content: .icon(image: UIImage(bundleImageName: "Settings/WallpaperAdjustments"), size: CGSize(width: 28.0, height: 28.0)), dark: true) + self.editButtonNode.enableSaturation = true self.playButtonPlayImage = generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -256,6 +261,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.addSubnode(self.cancelButtonNode) self.addSubnode(self.shareButtonNode) self.addSubnode(self.dayNightButtonNode) + self.addSubnode(self.editButtonNode) self.imageNode.addSubnode(self.brightnessNode) @@ -267,6 +273,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.cancelButtonNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.shareButtonNode.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside) self.dayNightButtonNode.addTarget(self, action: #selector(self.dayNightPressed), forControlEvents: .touchUpInside) + self.editButtonNode.addTarget(self, action: #selector(self.editPressed), forControlEvents: .touchUpInside) sliderValueChangedImpl = { [weak self] value in if let self { @@ -288,7 +295,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } switch entry { case .asset, .contextResult: - return self.cropNode.cropRect + 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 + } default: return nil } @@ -453,6 +466,70 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.switchTheme() } + @objc private func editPressed() { + guard let image = self.imageNode.image 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) + } + } + + 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?.imageNode.setSignal(.single({ arguments in + let context = DrawingContext(size: arguments.drawingSize, opaque: false) + context?.withFlippedContext({ context in + if let cgImage = result.cgImage { + context.draw(cgImage, in: CGRect(origin: .zero, size: arguments.drawingSize)) + } + }) + return context + })) + }) + } + + private var originalImage: UIImage? + public private(set) var editedImage: UIImage? + private var currentAdjustments: TGMediaEditAdjustments? + private func animateIntensityChange(delay: Double) { let targetValue: CGFloat = self.sliderNode.value self.sliderNode.internalUpdateLayout(size: self.sliderNode.frame.size, value: 1.0) @@ -484,11 +561,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.dismiss() } - func setEntry(_ entry: WallpaperGalleryEntry, arguments: WallpaperGalleryItemArguments, source: WallpaperListSource, mode: WallpaperGalleryController.Mode) { + func setEntry(_ entry: WallpaperGalleryEntry, arguments: WallpaperGalleryItemArguments, source: WallpaperListSource, mode: WallpaperGalleryController.Mode, interaction: WallpaperGalleryInteraction) { let previousArguments = self.arguments self.arguments = arguments self.source = source self.mode = mode + self.interaction = interaction if self.arguments.colorPreview != previousArguments.colorPreview { if self.arguments.colorPreview { @@ -530,6 +608,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners) + var isColor = false switch entry { case let .wallpaper(wallpaper, _): Queue.mainQueue().justDispatch { @@ -545,6 +624,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } else { self.playButtonNode.setIcon(self.playButtonRotateImage) } + isColor = true } else if case let .gradient(gradient) = wallpaper { self.nativeNode.isHidden = false self.nativeNode.update(wallpaper: wallpaper) @@ -555,10 +635,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } else { self.playButtonNode.setIcon(self.playButtonRotateImage) } + isColor = true } else if case .color = wallpaper { self.nativeNode.isHidden = false self.nativeNode.update(wallpaper: wallpaper) self.patternButtonNode.isSelected = false + isColor = true } else { self.nativeNode._internalUpdateIsSettingUpWallpaper() self.nativeNode.isHidden = true @@ -575,8 +657,21 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.patternButtonNode.isSelected = false self.playButtonNode.setIcon(self.playButtonRotateImage) } + + self.cancelButtonNode.enableSaturation = isColor + self.dayNightButtonNode.enableSaturation = isColor + self.editButtonNode.enableSaturation = isColor + self.shareButtonNode.enableSaturation = isColor + self.patternButtonNode.backgroundNode.enableSaturation = isColor + self.blurButtonNode.backgroundNode.enableSaturation = isColor + self.motionButtonNode.backgroundNode.enableSaturation = isColor + self.colorsButtonNode.backgroundNode.enableSaturation = isColor + self.playButtonNode.enableSaturation = isColor var canShare = false + var canSwitchTheme = false + var canEdit = false + switch entry { case let .wallpaper(wallpaper, message): self.initialWallpaper = wallpaper @@ -749,7 +844,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3)) self.wrapperNode.addSubnode(self.cropNode) showPreviewTooltip = true - self.serviceBackgroundNode.isHidden = false + canSwitchTheme = true + canEdit = true case let .contextResult(result): var imageDimensions: CGSize? var imageResource: TelegramMediaResource? @@ -805,11 +901,24 @@ final class WallpaperGalleryItemNode: GalleryItemNode { subtitleSignal = .single(nil) self.wrapperNode.addSubnode(self.cropNode) showPreviewTooltip = true - self.serviceBackgroundNode.isHidden = false + canSwitchTheme = true } self.contentSize = contentSize - self.shareButtonNode.isHidden = !canShare + if case .wallpaper = source { + canSwitchTheme = true + } else if case let .list(_, _, type) = source, case .colors = type { + canSwitchTheme = true + } + + if canSwitchTheme { + self.dayNightButtonNode.isHidden = false + self.shareButtonNode.isHidden = true + } else { + self.dayNightButtonNode.isHidden = true + self.shareButtonNode.isHidden = !canShare + } + self.editButtonNode.isHidden = !canEdit if self.cropNode.supernode == nil { self.imageNode.contentMode = .scaleAspectFill @@ -1157,16 +1266,22 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0)) var additionalYOffset: CGFloat = 0.0 + var canEditIntensity = false if let source = self.source { switch source { case .asset, .contextResult: - if self.isDarkAppearance { - additionalYOffset -= 44.0 + canEditIntensity = true + case let .wallpaper(wallpaper, _, _, _, _, _): + if case let .file(file) = wallpaper, !file.isPattern { + canEditIntensity = true } default: break } } + if canEditIntensity && self.isDarkAppearance { + additionalYOffset -= 44.0 + } let buttonSpacing: CGFloat = 18.0 @@ -1201,13 +1316,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } else { sliderFrame = sliderFrame.offsetBy(dx: 0.0, dy: 22.0) } - - var dayNightHidden = true - + let cancelSize = self.cancelButtonNode.measure(layout.size) let cancelFrame = CGRect(origin: CGPoint(x: 16.0 + offset.x, y: 16.0), size: cancelSize) - let shareFrame = CGRect(origin: CGPoint(x: layout.size.width - 16.0 - 28.0 + offset.x, y: 16.0), size: CGSize(width: 28.0, height: 28.0)) + let editFrame = CGRect(origin: CGPoint(x: layout.size.width - 16.0 - 28.0 + offset.x - 46.0, y: 16.0), size: CGSize(width: 28.0, height: 28.0)) let centerOffset: CGFloat = 32.0 @@ -1218,13 +1331,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode { blurFrame = leftButtonFrame motionAlpha = 1.0 motionFrame = rightButtonFrame - dayNightHidden = false case .contextResult: blurAlpha = 1.0 blurFrame = leftButtonFrame motionAlpha = 1.0 motionFrame = rightButtonFrame - dayNightHidden = false case let .wallpaper(wallpaper, _): switch wallpaper { case .builtin: @@ -1317,8 +1428,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame) transition.updateFrame(node: self.shareButtonNode, frame: shareFrame) transition.updateFrame(node: self.dayNightButtonNode, frame: shareFrame) - - self.dayNightButtonNode.isHidden = dayNightHidden + transition.updateFrame(node: self.editButtonNode, frame: editFrame) } private func updateMessagesLayout(layout: ContainerViewLayout, offset: CGPoint, transition: ContainedViewLayoutTransition) { @@ -1342,6 +1452,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { currentWallpaper = wallpaper } + var canEditIntensity = false if let source = self.source { switch source { case .slug, .wallpaper: @@ -1364,6 +1475,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { if hasAnimatableGradient { bottomMessageText = presentationData.strings.WallpaperPreview_PreviewBottomTextAnimatable } + + if case let .wallpaper(wallpaper, _, _, _, _, _) = source, case let .file(file) = wallpaper, !file.isPattern { + canEditIntensity = true + } case let .list(_, _, type): switch type { case .wallpapers: @@ -1393,9 +1508,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { case .asset, .contextResult: topMessageText = presentationData.strings.WallpaperPreview_CropTopText bottomMessageText = presentationData.strings.WallpaperPreview_CropBottomText - if self.isDarkAppearance { - bottomInset += 44.0 - } + canEditIntensity = true case .customColor: topMessageText = presentationData.strings.WallpaperPreview_CustomColorTopText bottomMessageText = presentationData.strings.WallpaperPreview_CustomColorBottomText @@ -1409,6 +1522,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { serviceMessageText = presentationData.strings.WallpaperPreview_NotAppliedInfo(peer.compactDisplayTitle).string } } + + if canEditIntensity && self.isDarkAppearance { + bottomInset += 44.0 + } let theme = self.presentationData.theme @@ -1567,7 +1684,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) { strongSelf.displayedPreviewTooltip = true - let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode, style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.45)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in + let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode, style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.35)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in return .dismiss(consume: false) }) strongSelf.galleryController()?.present(controller, in: .current) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift index 25649e2875..beebc7850e 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift @@ -41,7 +41,7 @@ final class WallpaperLightButtonBackgroundNode: ASDisplayNode { private let lightNode: ASDisplayNode override init() { - self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.45), enableBlur: true, enableSaturation: false) + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.35), enableBlur: true, enableSaturation: false) self.overlayNode = ASDisplayNode() self.overlayNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.75) self.overlayNode.layer.compositingFilter = "overlayBlendMode" @@ -71,8 +71,15 @@ final class WallpaperLightButtonBackgroundNode: ASDisplayNode { final class WallpaperOptionBackgroundNode: ASDisplayNode { private let backgroundNode: NavigationBackgroundNode + var enableSaturation: Bool { + didSet { + self.backgroundNode.updateColor(color: UIColor(rgb: 0x333333, alpha: 0.35), enableBlur: true, enableSaturation: self.enableSaturation, transition: .immediate) + } + } + init(enableSaturation: Bool = false) { - self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.45), enableBlur: true, enableSaturation: enableSaturation) + self.enableSaturation = enableSaturation + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.35), enableBlur: true, enableSaturation: enableSaturation) super.init() @@ -97,7 +104,13 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { case dayNight(isNight: Bool) } - var enableSaturation: Bool = false + var enableSaturation: Bool = false { + didSet { + if let backgroundNode = self.backgroundNode as? WallpaperOptionBackgroundNode { + backgroundNode.enableSaturation = self.enableSaturation + } + } + } private let content: Content var dark: Bool { @@ -128,7 +141,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { self.dark = dark if dark { - self.backgroundNode = WallpaperOptionBackgroundNode() + self.backgroundNode = WallpaperOptionBackgroundNode(enableSaturation: self.enableSaturation) } else { self.backgroundNode = WallpaperLightButtonBackgroundNode() } @@ -252,7 +265,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { - private let backgroundNode: WallpaperOptionBackgroundNode + let backgroundNode: WallpaperOptionBackgroundNode private let checkNode: CheckNode private let colorNode: ASImageNode @@ -488,7 +501,7 @@ final class WallpaperSliderNode: ASDisplayNode { self.value = value self.valueChanged = valueChanged - self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.45), enableBlur: true, enableSaturation: false) + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.35), enableBlur: true, enableSaturation: false) self.foregroundNode = ASDisplayNode() self.foregroundNode.clipsToBounds = true diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift index dc247c1097..406394d937 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift @@ -124,18 +124,8 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager guard let inputPeer = apiInputPeer(peer) else { 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? if let inputWallpaperAndInputSettings = wallpaper?.apiInputWallpaperAndSettings { @@ -149,10 +139,19 @@ func _internal_setChatWallpaper(postbox: Postbox, network: Network, stateManager return .complete() } |> mapToSignal { updates -> Signal in - if applyUpdates { - stateManager.addUpdates(updates) + 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 } - return .single(updates) } } |> switchToLatest } diff --git a/submodules/TelegramPresentationData/BUILD b/submodules/TelegramPresentationData/BUILD index d15b450263..75689afad7 100644 --- a/submodules/TelegramPresentationData/BUILD +++ b/submodules/TelegramPresentationData/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/StringPluralization:StringPluralization", "//submodules/Sunrise:Sunrise", "//submodules/TinyThumbnail:TinyThumbnail", + "//submodules/FastBlur:FastBlur", "//Telegram:PresentationStrings", ], visibility = [ diff --git a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift index a410693ee4..884cae8f3a 100644 --- a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift @@ -8,6 +8,7 @@ import Postbox import MediaResources import AppBundle import TinyThumbnail +import FastBlur private var backgroundImageForWallpaper: (TelegramWallpaper, Bool, UIImage)? @@ -200,8 +201,25 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } else if !didOutputBlurred { didOutputBlurred = true if let immediateThumbnailData = file.file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { - if let image = UIImage(data: decodedData)?.precomposed() { - subscriber.putNext((image, false)) + if let thumbnailImage = UIImage(data: decodedData)?.precomposed() { + let thumbnailContextSize = CGSize(width: thumbnailImage.size.width * 6.0, height: thumbnailImage.size.height * 6.0) + guard let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) else { + subscriber.putNext((thumbnailImage, false)) + return + } + thumbnailContext.withFlippedContext { c in + if let image = thumbnailImage.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + } + telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + if let blurredThumbnailImage = thumbnailContext.generateImage() { + subscriber.putNext((blurredThumbnailImage, false)) + } else { + subscriber.putNext((thumbnailImage, false)) + } } } } @@ -238,8 +256,25 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } else if !didOutputBlurred { didOutputBlurred = true if let immediateThumbnailData = file.file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { - if let image = UIImage(data: decodedData)?.precomposed() { - subscriber.putNext((image, false)) + if let thumbnailImage = UIImage(data: decodedData)?.precomposed() { + let thumbnailContextSize = CGSize(width: thumbnailImage.size.width * 6.0, height: thumbnailImage.size.height * 6.0) + guard let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) else { + subscriber.putNext((thumbnailImage, false)) + return + } + thumbnailContext.withFlippedContext { c in + if let image = thumbnailImage.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + } + telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + if let blurredThumbnailImage = thumbnailContext.generateImage() { + subscriber.putNext((blurredThumbnailImage, false)) + } else { + subscriber.putNext((thumbnailImage, false)) + } } } } diff --git a/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/Contents.json new file mode 100644 index 0000000000..5d62e00060 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "heart_18.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/heart_18.pdf b/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/heart_18.pdf new file mode 100644 index 0000000000..d65614e11f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Grid/Favorite.imageset/heart_18.pdf @@ -0,0 +1,75 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 2.625000 2.815430 cm +0.000000 0.000000 0.000000 scn +6.375000 0.000042 m +6.539109 0.000042 6.772649 0.119967 6.962005 0.239893 c +10.490347 2.512170 12.750000 5.175784 12.750000 7.877269 c +12.750000 10.193728 11.153094 11.809570 9.139605 11.809570 c +7.883540 11.809570 6.943069 11.115263 6.375000 10.073803 c +5.819555 11.108952 4.872772 11.809570 3.616708 11.809570 c +1.603218 11.809570 0.000000 10.193728 0.000000 7.877269 c +0.000000 5.175784 2.259654 2.512170 5.787995 0.239893 c +5.983664 0.119967 6.217203 0.000042 6.375000 0.000042 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 611 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 18.000000 18.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000701 00000 n +0000000723 00000 n +0000000896 00000 n +0000000970 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1029 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/Contents.json new file mode 100644 index 0000000000..962ddea299 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "edit.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/edit.pdf b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/edit.pdf new file mode 100644 index 0000000000..0259952bc5 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperAdjustments.imageset/edit.pdf @@ -0,0 +1,101 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 7.170227 6.170044 cm +1.000000 1.000000 1.000000 scn +4.830000 16.659882 m +5.288396 16.659882 5.660000 16.288279 5.660000 15.829882 c +5.660000 13.659912 l +14.830000 13.659912 l +15.288396 13.659912 15.660000 13.288309 15.660000 12.829912 c +15.660000 12.371515 15.288396 11.999912 14.830000 11.999912 c +5.660000 11.999912 l +5.660000 9.829882 l +5.660000 9.371485 5.288396 8.999882 4.830000 8.999882 c +4.371603 8.999882 4.000000 9.371485 4.000000 9.829882 c +4.000000 11.999912 l +0.830000 11.999912 l +0.371604 11.999912 0.000000 12.371515 0.000000 12.829912 c +0.000000 13.288309 0.371604 13.659912 0.830000 13.659912 c +4.000000 13.659912 l +4.000000 15.829882 l +4.000000 16.288279 4.371603 16.659882 4.830000 16.659882 c +h +14.830078 2.999956 m +15.288474 2.999956 15.660078 3.371560 15.660078 3.829956 c +15.660078 4.288352 15.288474 4.659956 14.830078 4.659956 c +11.660078 4.659956 l +11.660078 6.829987 l +11.660078 7.288383 11.288474 7.659986 10.830078 7.659986 c +10.371682 7.659986 10.000078 7.288383 10.000078 6.829987 c +10.000078 4.659956 l +0.830078 4.659956 l +0.371682 4.659956 0.000078 4.288352 0.000078 3.829956 c +0.000078 3.371560 0.371682 2.999956 0.830078 2.999956 c +10.000078 2.999956 l +10.000078 0.829987 l +10.000078 0.371590 10.371682 -0.000013 10.830078 -0.000013 c +11.288474 -0.000013 11.660078 0.371590 11.660078 0.829987 c +11.660078 2.999956 l +14.830078 2.999956 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1448 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001538 00000 n +0000001561 00000 n +0000001734 00000 n +0000001808 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1867 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6bf258998d..cd6de0c56d 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -858,9 +858,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.chatDisplayNode.dismissInput() let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, nil, [], nil, nil, nil), mode: .peer(EnginePeer(peer), true)) - wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _ in + wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, _ in if case let .wallpaper(wallpaper, _) = entry, case let .file(file) = wallpaper, !file.isPattern && options.contains(.blur) { - uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: entry, mode: options, cropRect: nil, brightness: nil, peerId: message.id.peerId, completion: { + uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: entry, mode: options, editedImage: nil, cropRect: nil, brightness: nil, peerId: message.id.peerId, completion: { wallpaperPreviewController?.dismiss() }) } else { @@ -1096,7 +1096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { transitionCompletion() }, getCaptionPanelView: { [weak self] in return self?.getCaptionPanelView() @@ -3970,7 +3970,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: inputText, snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: inputText, snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in return self?.getCaptionPanelView() }, sendMessagesWithSignals: { [weak self] signals, _, _ in if let strongSelf = self { @@ -5830,8 +5830,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let themeEmoticon: Signal = self.chatThemeEmoticonPromise.get() |> distinctUntilChanged + + let uploadingChatWallpaper: Signal + if let peerId = self.chatLocation.peerId { + uploadingChatWallpaper = self.context.account.pendingPeerMediaUploadManager.uploadingPeerMedia + |> map { uploadingPeerMedia -> TelegramWallpaper? in + if let item = uploadingPeerMedia[peerId], case let .wallpaper(wallpaper) = item.content { + return wallpaper + } else { + return nil + } + } + |> distinctUntilChanged + } else { + uploadingChatWallpaper = .single(nil) + } - let chatWallpaper: Signal = self.chatWallpaperPromise.get() + let chatWallpaper: Signal = combineLatest(self.chatWallpaperPromise.get(), uploadingChatWallpaper) + |> map { chatWallpaper, uploadingChatWallpaper in + return uploadingChatWallpaper ?? chatWallpaper + } |> distinctUntilChanged let themeSettings = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) @@ -18615,9 +18633,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, options, cropRect, brightness in + controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness in if let strongSelf = self { - uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, peerId: peerId, completion: { + uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, peerId: peerId, completion: { dismissControllers() }) } diff --git a/submodules/TelegramUI/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift index 213d5c2789..cec115a682 100644 --- a/submodules/TelegramUI/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift @@ -46,6 +46,7 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode self.subtitleNode.displaysAsynchronously = false self.imageNode = TransformImageNode() + self.imageNode.contentAnimations = [.subsequentUpdates] self.buttonNode = HighlightTrackingButtonNode() self.buttonNode.clipsToBounds = true diff --git a/submodules/TelegramUI/Sources/ChatMessageWallpaperBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWallpaperBubbleContentNode.swift index 589677da9a..2423b08455 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWallpaperBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWallpaperBubbleContentNode.swift @@ -16,12 +16,16 @@ import PhotoResources import WallpaperResources import Markdown import RadialStatusNode +import ComponentFlow +import AudioTranscriptionPendingIndicatorComponent class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { private var mediaBackgroundContent: WallpaperBubbleBackgroundNode? private let mediaBackgroundNode: NavigationBackgroundNode private let subtitleNode: TextNode + private let progressNode: ImmediateTextNode private let imageNode: TransformImageNode + private var transcriptionPendingIndicator: ComponentHostView? private var statusOverlayNode: ASDisplayNode private var statusNode: RadialStatusNode @@ -43,7 +47,12 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { self.subtitleNode.isUserInteractionEnabled = false self.subtitleNode.displaysAsynchronously = false + self.progressNode = ImmediateTextNode() + self.progressNode.isUserInteractionEnabled = false + self.progressNode.displaysAsynchronously = false + self.imageNode = TransformImageNode() + self.imageNode.contentAnimations = [.subsequentUpdates] self.buttonNode = HighlightTrackingButtonNode() self.buttonNode.clipsToBounds = true @@ -66,6 +75,7 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { self.addSubnode(self.mediaBackgroundNode) self.addSubnode(self.subtitleNode) + self.addSubnode(self.progressNode) self.addSubnode(self.imageNode) self.addSubnode(self.buttonNode) @@ -172,14 +182,25 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { } private func updateProgress(_ progress: Float?) { + guard let item = self.item else { + return + } let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut) if let progress { let progressValue = CGFloat(max(0.027, progress)) self.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: progressValue, cancelEnabled: true, animateRotation: true)) transition.updateAlpha(node: self.statusOverlayNode, alpha: 1.0) + + let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText + self.progressNode.attributedText = NSAttributedString(string: "\(Int(progress * 100.0))%", font: Font.semibold(13.0), textColor: primaryTextColor, paragraphAlignment: .center) + let progressSize = self.progressNode.updateLayout(CGSize(width: 100.0, height: 100.0)) + let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(self.subtitleNode.frame.midX - progressSize.width / 2.0), y: self.subtitleNode.frame.maxY + 1.0), size: progressSize) + self.progressNode.isHidden = false + self.progressNode.frame = progressFrame } else { self.statusNode.transitionToState(.none) transition.updateAlpha(node: self.statusOverlayNode, alpha: 0.0) + self.progressNode.isHidden = true } } @@ -218,8 +239,14 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0).compactDisplayTitle } ?? "" let text: String + var displayTrailingAnimatedDots = false if fromYou { - text = item.presentationData.strings.Notification_YouChangedWallpaper + if item.message.id.namespace == Namespaces.Message.Local { + text = item.presentationData.strings.Notification_YouChangingWallpaper + displayTrailingAnimatedDots = true + } else { + text = item.presentationData.strings.Notification_YouChangedWallpaper + } } else { text = item.presentationData.strings.Notification_ChangedWallpaper(peerName).string } @@ -227,15 +254,24 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor) let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: primaryTextColor) - let subtitle = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in + var subtitle = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) + if displayTrailingAnimatedDots { + let modifiedString = NSMutableAttributedString(attributedString: subtitle) + modifiedString.append(NSAttributedString(string: "...", font: Font.regular(13.0), textColor: .clear)) + subtitle = modifiedString + } let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_Wallpaper_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) - let backgroundSize = CGSize(width: width, height: subtitleLayout.size.height + 140.0 + (fromYou ? 0.0 : 42.0)) + var textHeight = subtitleLayout.size.height + if displayTrailingAnimatedDots { + textHeight += subtitleLayout.size.height + } + let backgroundSize = CGSize(width: width, height: textHeight + 140.0 + (fromYou ? 0.0 : 42.0)) return (backgroundSize.width, { boundingWidth in return (backgroundSize, { [weak self] animation, synchronousLoads, _ in @@ -324,6 +360,34 @@ class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode { let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 127.0), size: subtitleLayout.size) strongSelf.subtitleNode.frame = subtitleFrame + if displayTrailingAnimatedDots { + let transcriptionPendingIndicator: ComponentHostView + if let current = strongSelf.transcriptionPendingIndicator { + transcriptionPendingIndicator = current + } else { + transcriptionPendingIndicator = ComponentHostView() + strongSelf.transcriptionPendingIndicator = transcriptionPendingIndicator + strongSelf.view.addSubview(transcriptionPendingIndicator) + } + + let indicatorComponent: AnyComponent + indicatorComponent = AnyComponent(AudioTranscriptionPendingLottieIndicatorComponent(color: primaryTextColor, font: Font.regular(13.0))) + + let indicatorSize = transcriptionPendingIndicator.update( + transition: .immediate, + component: indicatorComponent, + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + + transcriptionPendingIndicator.frame = CGRect(origin: CGPoint(x: strongSelf.subtitleNode.frame.midX + subtitleLayout.trailingLineWidth / 2.0 - indicatorSize.width + 2.0 - UIScreenPixel, y: strongSelf.subtitleNode.frame.maxY - indicatorSize.height - 3.0 - UIScreenPixel), size: indicatorSize) + } else { + if let transcriptionPendingIndicator = strongSelf.transcriptionPendingIndicator { + strongSelf.transcriptionPendingIndicator = nil + transcriptionPendingIndicator.removeFromSuperview() + } + } + let buttonTitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonTitleLayout.size.width) / 2.0), y: subtitleFrame.maxY + 18.0), size: buttonTitleLayout.size) strongSelf.buttonTitleNode.frame = buttonTitleFrame diff --git a/submodules/TelegramUI/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Sources/ChatThemeScreen.swift index 035a156600..1ca1e7719e 100644 --- a/submodules/TelegramUI/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatThemeScreen.swift @@ -20,27 +20,7 @@ import TooltipUI import AnimatedStickerNode import TelegramAnimatedStickerNode import ShimmerEffect - -private func closeButtonImage(theme: PresentationTheme) -> UIImage? { - return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(UIColor(rgb: 0x808084, alpha: 0.1).cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setStrokeColor(theme.actionSheet.inputClearButtonColor.cgColor) - - context.move(to: CGPoint(x: 10.0, y: 10.0)) - context.addLine(to: CGPoint(x: 20.0, y: 20.0)) - context.strokePath() - - context.move(to: CGPoint(x: 20.0, y: 10.0)) - context.addLine(to: CGPoint(x: 10.0, y: 20.0)) - context.strokePath() - }) -} +import WebUI private struct ThemeSettingsThemeEntry: Comparable, Identifiable { let index: Int @@ -745,7 +725,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega private let contentBackgroundNode: ASDisplayNode private let titleNode: ASTextNode private let textNode: ImmediateTextNode - private let cancelButton: HighlightableButtonNode + private let cancelButtonNode: WebAppCancelButtonNode private let switchThemeButton: HighlightTrackingButtonNode private let animationContainerNode: ASDisplayNode private var animationNode: AnimationNode @@ -830,13 +810,13 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega self.contentBackgroundNode.backgroundColor = backgroundColor self.titleNode = ASTextNode() - self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Title, font: Font.semibold(16.0), textColor: textColor) + self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Title, font: Font.semibold(17.0), textColor: textColor) self.textNode = ImmediateTextNode() - self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Subtitle(peerName).string, font: Font.regular(12.0), textColor: secondaryTextColor) + self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Subtitle(peerName).string, font: Font.regular(15.0), textColor: secondaryTextColor) + self.textNode.isHidden = true - self.cancelButton = HighlightableButtonNode() - self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) + self.cancelButtonNode = WebAppCancelButtonNode(theme: self.presentationData.theme, strings: self.presentationData.strings) self.switchThemeButton = HighlightTrackingButtonNode() self.animationContainerNode = ASDisplayNode() @@ -845,7 +825,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega self.animationNode = AnimationNode(animation: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme), scale: 1.0) self.animationNode.isUserInteractionEnabled = false - self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, gloss: false) + self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 11.0, gloss: false) self.otherButton = HighlightableButtonNode() @@ -872,7 +852,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega self.backgroundNode.addSubnode(self.effectNode) self.backgroundNode.addSubnode(self.contentBackgroundNode) self.contentContainerNode.addSubnode(self.titleNode) - self.contentContainerNode.addSubnode(self.textNode) + self.buttonsContentContainerNode.addSubnode(self.textNode) self.buttonsContentContainerNode.addSubnode(self.doneButton) self.buttonsContentContainerNode.addSubnode(self.otherButton) @@ -880,10 +860,10 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega self.animationContainerNode.addSubnode(self.animationNode) self.topContentContainerNode.addSubnode(self.switchThemeButton) self.topContentContainerNode.addSubnode(self.listNode) - self.topContentContainerNode.addSubnode(self.cancelButton) + self.topContentContainerNode.addSubnode(self.cancelButtonNode) self.switchThemeButton.addTarget(self, action: #selector(self.switchThemePressed), forControlEvents: .touchUpInside) - self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) + self.cancelButtonNode.buttonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) self.doneButton.pressed = { [weak self] in if let strongSelf = self { strongSelf.doneButton.isUserInteractionEnabled = false @@ -970,6 +950,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } } } + + self.updateCancelButton() } private func enqueueTransition(_ transition: ThemeSettingsThemeItemNodeTransition) { @@ -1018,6 +1000,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega UIView.transition(with: self.buttonsContentContainerNode.view, duration: ChatThemeScreen.themeCrossfadeDuration, options: [.transitionCrossDissolve, .curveLinear]) { self.updateButtons() } + self.updateCancelButton() self.skipButtonsUpdate = false self.themeSelectionsCount += 1 @@ -1027,23 +1010,17 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } private func updateButtons() { - let canResetWallpaper = self.controller?.canResetWallpaper ?? false - let doneButtonTitle: String - let otherButtonTitle: String var accentButtonTheme = true - var destructiveOtherButton = false + var otherIsEnabled = false if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { doneButtonTitle = self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper - otherButtonTitle = canResetWallpaper ? self.presentationData.strings.Conversation_Theme_ResetWallpaper : self.presentationData.strings.Common_Cancel + otherIsEnabled = self.controller?.canResetWallpaper == true accentButtonTheme = false - destructiveOtherButton = canResetWallpaper } else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil { doneButtonTitle = self.presentationData.strings.Conversation_Theme_Reset - otherButtonTitle = self.presentationData.strings.Conversation_Theme_OtherOptions } else { - doneButtonTitle = self.presentationData.strings.Conversation_Theme_ApplyBackground - otherButtonTitle = self.presentationData.strings.Conversation_Theme_OtherOptions + doneButtonTitle = self.presentationData.strings.Conversation_Theme_Apply } let buttonTheme: SolidRoundedButtonTheme @@ -1058,7 +1035,25 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } self.doneButton.updateTheme(buttonTheme) - self.otherButton.setTitle(otherButtonTitle, with: Font.regular(17.0), with: destructiveOtherButton ? self.presentationData.theme.actionSheet.destructiveActionTextColor : self.presentationData.theme.actionSheet.controlAccentColor, for: .normal) + self.otherButton.setTitle(self.presentationData.strings.Conversation_Theme_ResetWallpaper, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.destructiveActionTextColor, for: .normal) + self.otherButton.isHidden = !otherIsEnabled + self.textNode.isHidden = !accentButtonTheme || self.controller?.canResetWallpaper == false + + if let (layout, navigationBarHeight) = self.containerLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } + } + + private func updateCancelButton() { + var cancelButtonState: WebAppCancelButtonNode.State = .cancel + if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { + + } else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil { + cancelButtonState = .back + } else { + cancelButtonState = .back + } + self.cancelButtonNode.setState(cancelButtonState, animated: true) } private var switchThemeIconAnimator: DisplayLinkAnimator? @@ -1069,14 +1064,14 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega let previousTheme = self.presentationData.theme self.presentationData = presentationData - self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.semibold(16.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) - self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(12.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.semibold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(15.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor) if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout { self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } - self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) + self.cancelButtonNode.theme = presentationData.theme let previousIconColors = iconColors(theme: previousTheme) let newIconColors = iconColors(theme: self.presentationData.theme) @@ -1113,7 +1108,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } @objc func cancelButtonPressed() { - self.cancel?() + if self.cancelButtonNode.state == .back { + self.setEmoticon(self.initiallySelectedEmoticon) + } else { + self.cancel?() + } } @objc func otherButtonPressed() { @@ -1317,7 +1316,10 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega let bottomInset: CGFloat = 10.0 + cleanInsets.bottom let titleHeight: CGFloat = 54.0 - let contentHeight = titleHeight + bottomInset + 188.0 + 50.0 + var contentHeight = titleHeight + bottomInset + 168.0 + if self.controller?.canResetWallpaper == true { + contentHeight += 50.0 + } let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0) @@ -1336,29 +1338,38 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) let titleSize = self.titleNode.measure(CGSize(width: width - 90.0, height: titleHeight)) - let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 11.0 + UIScreenPixel), size: titleSize) + let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 18.0 + UIScreenPixel), size: titleSize) transition.updateFrame(node: self.titleNode, frame: titleFrame) - let textSize = self.textNode.updateLayout(CGSize(width: width - 90.0, height: titleHeight)) - let textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: 31.0), size: textSize) - transition.updateFrame(node: self.textNode, frame: textFrame) - let switchThemeSize = CGSize(width: 44.0, height: 44.0) - let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize) + let switchThemeFrame = CGRect(origin: CGPoint(x: contentFrame.width - switchThemeSize.width - 3.0, y: 6.0), size: switchThemeSize) transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame) transition.updateFrame(node: self.animationContainerNode, frame: switchThemeFrame.insetBy(dx: 9.0, dy: 9.0)) transition.updateFrameAsPositionAndBounds(node: self.animationNode, frame: CGRect(origin: .zero, size: self.animationContainerNode.frame.size)) - let cancelSize = CGSize(width: 44.0, height: 44.0) - let cancelFrame = CGRect(origin: CGPoint(x: contentFrame.width - cancelSize.width - 3.0, y: 6.0), size: cancelSize) - transition.updateFrame(node: self.cancelButton, frame: cancelFrame) - + let cancelSize = self.cancelButtonNode.calculateSizeThatFits(CGSize(width: layout.size.width, height: 56.0)) + let cancelFrame = CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: cancelSize) + transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame) + let buttonInset: CGFloat = 16.0 let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) - transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - 50.0 - insets.bottom - 6.0, width: contentFrame.width, height: doneButtonHeight)) + var doneY = contentHeight - doneButtonHeight - 2.0 - insets.bottom + if self.controller?.canResetWallpaper == true { + doneY = contentHeight - doneButtonHeight - 52.0 - insets.bottom + } + transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: doneY, width: contentFrame.width, height: doneButtonHeight)) - let colorButtonSize = self.otherButton.measure(CGSize(width: contentFrame.width - buttonInset * 2.0, height: .greatestFiniteMagnitude)) - transition.updateFrame(node: self.otherButton, frame: CGRect(origin: CGPoint(x: floor((contentFrame.width - colorButtonSize.width) / 2.0), y: contentHeight - colorButtonSize.height - insets.bottom - 6.0 - 9.0), size: colorButtonSize)) + let otherButtonSize = self.otherButton.measure(CGSize(width: contentFrame.width - buttonInset * 2.0, height: .greatestFiniteMagnitude)) + self.otherButton.frame = CGRect(origin: CGPoint(x: floor((contentFrame.width - otherButtonSize.width) / 2.0), y: contentHeight - otherButtonSize.height - insets.bottom - 15.0), size: otherButtonSize) + + let textSize = self.textNode.updateLayout(CGSize(width: width - 90.0, height: titleHeight)) + let textFrame: CGRect + if self.controller?.canResetWallpaper == true { + textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: contentHeight - textSize.height - insets.bottom - 17.0), size: textSize) + } else { + textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: contentHeight - textSize.height - insets.bottom - 15.0), size: textSize) + } + transition.updateFrame(node: self.textNode, frame: textFrame) transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame) transition.updateFrame(node: self.topContentContainerNode, frame: contentContainerFrame) @@ -1371,7 +1382,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega let contentSize = CGSize(width: contentFrame.width, height: 120.0) self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: contentSize.height, height: contentSize.width) - self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight + 6.0) + self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight - 4.0) self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 048961a0af..a095843d44 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3906,6 +3906,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate var previousAbout: String? var currentAbout: String? + var previousIsBlocked: Bool? + var currentIsBlocked: Bool? + var previousPhotoIsPersonal: Bool? var currentPhotoIsPersonal: Bool? if let previousUser = previousData?.peer as? TelegramUser { @@ -3932,6 +3935,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate currentVideoCallsAvailable = cachedData.videoCallsAvailable previousAbout = previousCachedData.about currentAbout = cachedData.about + previousIsBlocked = previousCachedData.isBlocked + currentIsBlocked = cachedData.isBlocked } if self.isSettings { @@ -3954,6 +3959,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate if let previousPhotoIsPersonal, let currentPhotoIsPersonal, previousPhotoIsPersonal != currentPhotoIsPersonal { infoUpdated = true } + if let previousIsBlocked, let currentIsBlocked, previousIsBlocked != currentIsBlocked { + infoUpdated = true + } self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: self.didSetReady && (membersUpdated || infoUpdated) ? .animated(duration: 0.3, curve: .spring) : .immediate) } } @@ -4091,7 +4099,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: message.associatedThreadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: message.associatedThreadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { transitionCompletion() }, getCaptionPanelView: { return nil @@ -5396,6 +5404,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate case .leave: self.openLeavePeer(delete: false) case .stop: + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: self.presentationData.strings.PeerInfo_BotBlockedTitle, text: self.presentationData.strings.PeerInfo_BotBlockedText, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) self.updateBlocked(block: true) } } diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 230b208e50..7076506512 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -1030,6 +1030,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } else { strongSelf.blurredBackgroundContents = nil } + strongSelf.updateBubbles() strongSelf._isReady.set(true) })) } diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index b85cdf200d..41c46e91b5 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -258,7 +258,7 @@ public func wallpaperImage(account: Account, accountManager: AccountManager