From 184a02baaa4e036e47cc8e74812bacc243e82e02 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Thu, 6 Apr 2023 15:37:27 +0400 Subject: [PATCH 1/4] update cached data after service messages for chat background --- .../State/AccountStateManagementUtils.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 511563eefc..e62c9497fe 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -3405,6 +3405,20 @@ func replayFinalState( for (space, _) in holesAtHistoryStart { transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id) } + case let .setChatWallpaper(wallpaper): + transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in + var current = current + if current == nil { + if peerId.namespace == Namespaces.Peer.CloudUser { + current = CachedUserData() + } + } + if let cachedData = current as? CachedUserData { + return cachedData.withUpdatedWallpaper(wallpaper) + } else { + return current + } + }) default: break } From 86d12b5624ea425dd026df70b1ad53e9b0bb1f12 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Thu, 6 Apr 2023 16:39:58 +0400 Subject: [PATCH 2/4] fix apply action --- .../State/AccountStateManagementUtils.swift | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index e62c9497fe..b2b16fb876 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -3406,19 +3406,21 @@ func replayFinalState( transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id) } case let .setChatWallpaper(wallpaper): - transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in - var current = current - if current == nil { - if peerId.namespace == Namespaces.Peer.CloudUser { - current = CachedUserData() - } - } - if let cachedData = current as? CachedUserData { - return cachedData.withUpdatedWallpaper(wallpaper) - } else { - return current - } - }) + if chatPeerId == accountPeerId { + transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in + var current = current + if current == nil { + if peerId.namespace == Namespaces.Peer.CloudUser { + current = CachedUserData() + } + } + if let cachedData = current as? CachedUserData { + return cachedData.withUpdatedWallpaper(wallpaper) + } else { + return current + } + }) + } default: break } From 858e6068646444e7a5d7c2815141ceedac108ec5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 6 Apr 2023 17:06:28 +0400 Subject: [PATCH 3/4] Chat wallpaper improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 7 + .../Sources/AttachmentController.swift | 4 +- .../Sources/AttachmentPanel.swift | 6 +- .../Sources/MediaAssetsContext.swift | 13 +- .../Sources/MediaPickerScreen.swift | 50 ++++- .../Sources/MediaPickerTitleView.swift | 2 +- submodules/SettingsUI/BUILD | 1 + .../Reactions/ReactionChatPreviewItem.swift | 5 +- .../Themes/ThemeColorsGridController.swift | 189 ++++++++++++++---- .../ThemeColorsGridControllerNode.swift | 121 +++++++---- .../Sources/Themes/ThemeGridController.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 19 +- 12 files changed, 309 insertions(+), 110 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 4f21a5b6a0..8c6579dc49 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9151,3 +9151,10 @@ Sorry for the inconvenience."; "Conversation.Theme.SetPhotoWallpaper" = "Choose Background from Photos"; "Conversation.Theme.SetColorWallpaper" = "Choose Color as a Background"; "Conversation.Theme.OtherOptions" = "Other Options..."; + +"Conversation.Theme.ChooseWallpaperTitle" = "Choose Background"; +"Conversation.Theme.ResetWallpaper" = "Reset to Default Background"; +"Conversation.Theme.ChooseColorTitle" = "Set a Color"; +"Conversation.Theme.SetCustomColor" = "Set Custom"; + +"Appearance.ShowNextMediaOnTap" = "Show Next Media on Tap"; diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 552aefab1c..504bcf075a 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -177,7 +177,7 @@ private func generateMaskImage() -> UIImage? { public class AttachmentController: ViewController { private let context: AccountContext private let updatedPresentationData: (initial: PresentationData, signal: Signal)? - private let chatLocation: ChatLocation + private let chatLocation: ChatLocation? private let buttons: [AttachmentButtonType] private let initialButton: AttachmentButtonType private let fromMenu: Bool @@ -888,7 +888,7 @@ public class AttachmentController: ViewController { public var getSourceRect: (() -> CGRect?)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation?, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { self.context = context self.updatedPresentationData = updatedPresentationData self.chatLocation = chatLocation diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 5e7cf29c0e..c6062f9f63 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -758,13 +758,13 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { var mainButtonPressed: () -> Void = { } - init(context: AccountContext, chatLocation: ChatLocation, updatedPresentationData: (initial: PresentationData, signal: Signal)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { + init(context: AccountContext, chatLocation: ChatLocation?, updatedPresentationData: (initial: PresentationData, signal: Signal)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { self.context = context self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } self.makeEntityInputView = makeEntityInputView - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil) self.containerNode = ASDisplayNode() self.containerNode.clipsToBounds = true @@ -936,7 +936,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { return } textInputPanelNode.loadTextInputNodeIfNeeded() - guard let textInputNode = textInputPanelNode.textInputNode, let peerId = chatLocation.peerId else { + guard let textInputNode = textInputPanelNode.textInputNode, let peerId = chatLocation?.peerId else { return } diff --git a/submodules/MediaPickerUI/Sources/MediaAssetsContext.swift b/submodules/MediaPickerUI/Sources/MediaAssetsContext.swift index 113bf267e6..aebaf1f84d 100644 --- a/submodules/MediaPickerUI/Sources/MediaAssetsContext.swift +++ b/submodules/MediaPickerUI/Sources/MediaAssetsContext.swift @@ -5,12 +5,16 @@ import Photos import AVFoundation class MediaAssetsContext: NSObject, PHPhotoLibraryChangeObserver { + private let assetType: PHAssetMediaType? + private var registeredChangeObserver = false private let changeSink = ValuePipe() private let mediaAccessSink = ValuePipe() private let cameraAccessSink = ValuePipe() - override init() { + init(assetType: PHAssetMediaType?) { + self.assetType = assetType + super.init() if PHPhotoLibrary.authorizationStatus() == .authorized { @@ -30,7 +34,12 @@ class MediaAssetsContext: NSObject, PHPhotoLibraryChangeObserver { } func fetchAssets(_ collection: PHAssetCollection) -> Signal, NoError> { - let initialFetchResult = PHAsset.fetchAssets(in: collection, options: nil) + let options = PHFetchOptions() + if let assetType = self.assetType { + options.predicate = NSPredicate(format: "mediaType = %d", assetType.rawValue) + } + + let initialFetchResult = PHAsset.fetchAssets(in: collection, options: options) let fetchResult = Atomic>(value: initialFetchResult) return .single(initialFetchResult) |> then( diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index eb9d6215d5..7da26bf271 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -128,7 +128,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } } - case assets(PHAssetCollection?, Bool) + public enum AssetsMode: Equatable { + case `default` + case wallpaper + } + + case assets(PHAssetCollection?, AssetsMode) case media([Media]) } @@ -238,7 +243,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.controller = controller self.presentationData = controller.presentationData - let mediaAssetsContext = MediaAssetsContext() + var assetType: PHAssetMediaType? + if case let .assets(_, mode) = controller.subject, case .wallpaper = mode { + assetType = .image + } + let mediaAssetsContext = MediaAssetsContext(assetType: assetType) self.mediaAssetsContext = mediaAssetsContext self.containerNode = ASDisplayNode() @@ -253,7 +262,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { super.init() - if case .assets(nil, false) = controller.subject { + if case .assets(nil, .default) = controller.subject { } else { self.preloadPromise.set(false) } @@ -454,7 +463,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } }) - if let controller = self.controller, case .assets(nil, false) = controller.subject { + if let controller = self.controller, case .assets(nil, .default) = controller.subject { let enableAnimations = self.controller?.context.sharedContext.energyUsageSettings.fullTranslucency ?? true let cameraView = TGAttachmentCameraView(forSelfPortrait: false, videoModeByDefault: controller.bannedSendPhotos != nil && controller.bannedSendVideos == nil)! @@ -556,7 +565,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { var updateLayout = false var selectable = true - if case let .assets(_, isStandalone) = controller.subject, isStandalone { + if case let .assets(_, mode) = controller.subject, mode != .default { selectable = false } @@ -1247,8 +1256,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0) - if case let .assets(collection, _) = subject, let collection = collection { - self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery + if case let .assets(collection, mode) = subject { + if let collection = collection { + self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery + } else { + switch mode { + case .default: + self.titleView.title = presentationData.strings.Attachment_Gallery + case .wallpaper: + self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle + } + } } else { self.titleView.title = presentationData.strings.Attachment_Gallery } @@ -1316,7 +1334,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.navigationItem.titleView = self.titleView - if case let .assets(_, isStandalone) = self.subject, isStandalone { + if case let .assets(_, mode) = self.subject, mode != .default { self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) } else { if case let .assets(collection, _) = self.subject, collection != nil { @@ -1674,13 +1692,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } private func presentSearch(activateOnDisplay: Bool) { - guard self.moreButtonNode.iconNode.iconState == .search else { + guard self.moreButtonNode.iconNode.iconState == .search, case let .assets(_, mode) = self.subject else { return } self.requestAttachmentMenuExpansion() self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in if let strongSelf = self { - let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, false), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) + let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, mode), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker @@ -1968,3 +1986,15 @@ public class MediaPickerGridSelectionGesture : UIPanGestureRecognizer { self.updateIsScrollEnabled(true) } } + +public func standaloneMediaPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, subject: MediaPickerScreen.Subject, completion: @escaping (PHAsset) -> Void = { _ in }) -> ViewController { + let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { + return nil + }) + controller.requestController = { _, present in + let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: subject) + mediaPickerController.customSelection = completion + present(mediaPickerController, mediaPickerController.mediaPickerContext) + } + return controller +} diff --git a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift index 81dd23cf02..7fe0210eeb 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift @@ -88,7 +88,7 @@ final class MediaPickerTitleView: UIView { let controlSize = self.segmentedControlNode.updateLayout(.stretchToFill(width: min(300.0, size.width - 36.0)), transition: .immediate) self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - controlSize.width) / 2.0), y: floorToScreenPixels((size.height - controlSize.height) / 2.0)), size: controlSize) - let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: 44.0)) + let titleSize = self.titleNode.updateLayout(CGSize(width: 210.0, height: 44.0)) self.titleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize) } } diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 678a8fd26f..d7c52ee05c 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -112,6 +112,7 @@ swift_library( "//submodules/FeaturedStickersScreen:FeaturedStickersScreen", "//submodules/MediaPickerUI:MediaPickerUI", "//submodules/ImageBlur:ImageBlur", + "//submodules/AttachmentUI:AttachmentUI", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift index 74293fd2ba..205059cf86 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift @@ -309,6 +309,9 @@ class ReactionChatPreviewItemNode: ListViewItemNode { if let node = node { contentSize.height += node.frame.size.height } + if item.reaction == nil { + contentSize.height += 34.0 + } insets = itemListNeighborsGroupedInsets(neighbors, params) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) @@ -333,7 +336,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) - var topOffset: CGFloat = 16.0 + var topOffset: CGFloat = 16.0 + 17.0 if let node = node { strongSelf.messageNode = node if node.supernode == nil { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift index bc402517a9..243de44ec9 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift @@ -9,6 +9,7 @@ import LegacyComponents import TelegramPresentationData import TelegramUIPreferences import AccountContext +import AttachmentUI private func availableGradients(theme: PresentationTheme) -> [[UInt32]] { if theme.overallDarkAppearance { @@ -102,7 +103,7 @@ private func availableColors(theme: PresentationTheme) -> [UInt32] { } } -public final class ThemeColorsGridController: ViewController { +public final class ThemeColorsGridController: ViewController, AttachmentContainable { public enum Mode { case `default` case peer(EnginePeer) @@ -145,6 +146,10 @@ public final class ThemeColorsGridController: ViewController { private var previousContentOffset: GridNodeVisibleContentOffset? + fileprivate let mainButtonStatePromise = Promise(nil) + + var pushController: (ViewController) -> Void = { _ in } + public init(context: AccountContext, mode: Mode = .default) { self.context = context self.mode = mode @@ -152,7 +157,6 @@ public final class ThemeColorsGridController: ViewController { super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) - self.title = self.presentationData.strings.WallpaperColors_Title self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.scrollToTop = { [weak self] in @@ -174,6 +178,18 @@ public final class ThemeColorsGridController: ViewController { } } }) + + if case .peer = mode { + self.title = self.presentationData.strings.Conversation_Theme_ChooseColorTitle + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Conversation_Theme_SetCustomColor, style: .plain, target: self, action: #selector(self.customPressed)) + } else { + self.title = self.presentationData.strings.WallpaperColors_Title + } + + self.pushController = { [weak self] controller in + self?.push(controller) + } } required public init(coder aDecoder: NSCoder) { @@ -184,6 +200,14 @@ public final class ThemeColorsGridController: ViewController { self.presentationDataDisposable?.dispose() } + @objc private func cancelPressed() { + self.dismiss() + } + + @objc private func customPressed() { + self.presentColorPicker() + } + private func updateThemeAndStrings() { self.title = self.presentationData.strings.WallpaperColors_Title self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -194,56 +218,68 @@ public final class ThemeColorsGridController: ViewController { } } + private func presentColorPicker() { + let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] sharedData in + guard let strongSelf = self else { + return + } + let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings + + let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered + let themeReference: PresentationThemeReference + if autoNightModeTriggered { + themeReference = settings.automaticThemeSwitchSetting.theme + } else { + themeReference = settings.theme + } + + let controller = ThemeAccentColorController(context: strongSelf.context, mode: .background(themeReference: themeReference), resultMode: strongSelf.mode.colorPickerMode) + controller.completion = { [weak self] in + if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { + var controllers = navigationController.viewControllers + controllers = controllers.filter { controller in + if controller is ThemeColorsGridController { + return false + } + return true + } + navigationController.setViewControllers(controllers, animated: false) + controllers = controllers.filter { controller in + if controller is ThemeAccentColorController { + return false + } + return true + } + navigationController.setViewControllers(controllers, animated: true) + } + } + strongSelf.pushController(controller) + }) + } + public override func loadDisplayNode() { self.displayNode = ThemeColorsGridControllerNode(context: self.context, presentationData: self.presentationData, controller: self, gradients: availableGradients(theme: self.presentationData.theme), colors: availableColors(theme: self.presentationData.theme), push: { [weak self] controller in - self?.push(controller) + self?.pushController(controller) }, pop: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { let _ = navigationController.popViewController(animated: true) } }, presentColorPicker: { [weak self] in if let strongSelf = self { - let _ = (strongSelf.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] sharedData in - guard let strongSelf = self else { - return - } - let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings - - let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered - let themeReference: PresentationThemeReference - if autoNightModeTriggered { - themeReference = settings.automaticThemeSwitchSetting.theme - } else { - themeReference = settings.theme - } - - let controller = ThemeAccentColorController(context: strongSelf.context, mode: .background(themeReference: themeReference), resultMode: strongSelf.mode.colorPickerMode) - controller.completion = { [weak self] in - if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - var controllers = navigationController.viewControllers - controllers = controllers.filter { controller in - if controller is ThemeColorsGridController { - return false - } - return true - } - navigationController.setViewControllers(controllers, animated: false) - controllers = controllers.filter { controller in - if controller is ThemeAccentColorController { - return false - } - return true - } - navigationController.setViewControllers(controllers, animated: true) - } - } - strongSelf.push(controller) - }) + strongSelf.presentColorPicker() } }) + let transitionOffset: CGFloat + switch self.mode { + case .default: + transitionOffset = 30.0 + case .peer: + transitionOffset = 2.0 + } + self.controllerNode.gridNode.visibleContentOffsetChanged = { [weak self] offset in if let strongSelf = self { var previousContentOffsetValue: CGFloat? @@ -253,12 +289,12 @@ public final class ThemeColorsGridController: ViewController { switch offset { case let .known(value): let transition: ContainedViewLayoutTransition - if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 { + if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > transitionOffset { transition = .animated(duration: 0.2, curve: .easeInOut) } else { transition = .immediate } - strongSelf.navigationBar?.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition) + strongSelf.navigationBar?.updateBackgroundAlpha(min(transitionOffset, value) / transitionOffset, transition: transition) case .unknown, .none: strongSelf.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate) } @@ -279,4 +315,71 @@ public final class ThemeColorsGridController: ViewController { self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) } + + @objc fileprivate func mainButtonPressed() { + + } + + public var requestAttachmentMenuExpansion: () -> Void = {} + public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in } + public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + public var cancelPanGesture: () -> Void = { } + public var isContainerPanning: () -> Bool = { return false } + public var isContainerExpanded: () -> Bool = { return false } + + public var mediaPickerContext: AttachmentMediaPickerContext? { + return ThemeColorsGridContext(controller: self) + } +} + +private final class ThemeColorsGridContext: AttachmentMediaPickerContext { + private weak var controller: ThemeColorsGridController? + + var selectionCount: Signal { + return .single(0) + } + + var caption: Signal { + return .single(nil) + } + + public var loadingProgress: Signal { + return .single(nil) + } + + public var mainButtonState: Signal { + return self.controller?.mainButtonStatePromise.get() ?? .single(nil) + } + + init(controller: ThemeColorsGridController) { + self.controller = controller + } + + func setCaption(_ caption: NSAttributedString) { + } + + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { + } + + func schedule() { + } + + func mainButtonAction() { + self.controller?.mainButtonPressed() + } +} + + +public func standaloneColorPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, push: @escaping (ViewController) -> Void) -> ViewController { + let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { + return nil + }) + controller.requestController = { _, present in + let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer)) + colorPickerController.pushController = { controller in + push(controller) + } + present(colorPickerController, colorPickerController.mediaPickerContext) + } + return controller } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift index 72d0a6ca95..94bbdcb6d1 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift @@ -73,7 +73,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { let ready = ValuePromise() - private var backgroundNode: ASDisplayNode + private var topBackgroundNode: ASDisplayNode private var separatorNode: ASDisplayNode private let customColorItemNode: ItemListActionItemNode @@ -102,8 +102,8 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { self.rightOverlayNode = ASDisplayNode() self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor - self.backgroundNode = ASDisplayNode() - self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor + self.topBackgroundNode = ASDisplayNode() + self.topBackgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor self.separatorNode = ASDisplayNode() self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor @@ -121,9 +121,14 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - self.gridNode.addSubnode(self.backgroundNode) - self.gridNode.addSubnode(self.separatorNode) - self.gridNode.addSubnode(self.customColorItemNode) + if case .default = controller.mode { + self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + self.gridNode.addSubnode(self.topBackgroundNode) + self.gridNode.addSubnode(self.separatorNode) + self.gridNode.addSubnode(self.customColorItemNode) + } else { + self.backgroundColor = presentationData.theme.list.plainBackgroundColor + } self.addSubnode(self.gridNode) let previousEntries = Atomic<[ThemeColorsGridControllerEntry]?>(value: nil) @@ -240,9 +245,13 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor - self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor + if let controller = self.controller, case .default = controller.mode { + self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor + self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor + } else { + self.backgroundColor = presentationData.theme.list.plainBackgroundColor + } self.customColorItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.WallpaperColors_SetCustomColor, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: { [weak self] in self?.presentColorPicker() @@ -272,6 +281,9 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + guard let controller = self.controller else { + return + } let hadValidLayout = self.validLayout != nil var insets = layout.insets(options: [.input]) @@ -280,7 +292,18 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { insets.right = layout.safeInsets.right let scrollIndicatorInsets = insets - let minSpacing: CGFloat = 8.0 + let itemsPerRow: Int + if case .compact = layout.metrics.widthClass { + switch layout.orientation { + case .portrait: + itemsPerRow = 3 + case .landscape: + itemsPerRow = 5 + } + } else { + itemsPerRow = 3 + } + let referenceImageSize: CGSize let screenWidth = min(layout.size.width, layout.size.height) if screenWidth >= 375.0 { @@ -288,29 +311,56 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { } else { referenceImageSize = CGSize(width: 91.0, height: 91.0) } - let imageCount = Int((layout.size.width - insets.left - insets.right - minSpacing * 2.0) / (referenceImageSize.width + minSpacing)) - let imageSize = referenceImageSize.aspectFilled(CGSize(width: floor((layout.size.width - CGFloat(imageCount + 1) * minSpacing) / CGFloat(imageCount)), height: referenceImageSize.height)) - let spacing = floor((layout.size.width - CGFloat(imageCount) * imageSize.width) / CGFloat(imageCount + 1)) + + let width = layout.size.width - layout.safeInsets.left - layout.safeInsets.right + let imageSize: CGSize + let spacing: CGFloat + var fillWidth: Bool? + if case .peer = controller.mode { + spacing = 1.0 + + let itemWidth = floorToScreenPixels((width - spacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow)) + imageSize = CGSize(width: itemWidth, height: itemWidth) + fillWidth = true + } else { + let minSpacing = 8.0 + + imageSize = referenceImageSize.aspectFilled(CGSize(width: floor((width - CGFloat(itemsPerRow + 1) * minSpacing) / CGFloat(itemsPerRow)), height: referenceImageSize.height)) + spacing = floor((width - CGFloat(itemsPerRow) * imageSize.width) / CGFloat(itemsPerRow + 1)) + } + + let buttonTopInset: CGFloat = 32.0 + let buttonHeight: CGFloat = 44.0 + let buttonBottomInset: CGFloat = 35.0 + + var buttonInset: CGFloat = buttonTopInset + buttonHeight + buttonBottomInset + var buttonOffset = buttonInset + 10.0 var listInsets = insets - if layout.size.width >= 375.0 { - let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) - listInsets.left += inset - listInsets.right += inset - - if self.leftOverlayNode.supernode == nil { - self.gridNode.addSubnode(self.leftOverlayNode) - } - if self.rightOverlayNode.supernode == nil { - self.gridNode.addSubnode(self.rightOverlayNode) + if case .default = controller.mode { + if layout.size.width >= 375.0 { + let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) + listInsets.left += inset + listInsets.right += inset + + if self.leftOverlayNode.supernode == nil { + self.gridNode.addSubnode(self.leftOverlayNode) + } + if self.rightOverlayNode.supernode == nil { + self.gridNode.addSubnode(self.rightOverlayNode) + } + } else { + if self.leftOverlayNode.supernode != nil { + self.leftOverlayNode.removeFromSupernode() + } + if self.rightOverlayNode.supernode != nil { + self.rightOverlayNode.removeFromSupernode() + } } } else { - if self.leftOverlayNode.supernode != nil { - self.leftOverlayNode.removeFromSupernode() - } - if self.rightOverlayNode.supernode != nil { - self.rightOverlayNode.removeFromSupernode() - } + self.customColorItemNode.isHidden = true + buttonOffset = 0.0 + buttonInset = 0.0 } let makeColorLayout = self.customColorItemNode.asyncLayout() @@ -318,14 +368,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { let (colorLayout, colorApply) = makeColorLayout(self.customColorItem, params, ItemListNeighbors(top: .none, bottom: .none)) colorApply() - let buttonTopInset: CGFloat = 32.0 - let buttonHeight: CGFloat = 44.0 - let buttonBottomInset: CGFloat = 35.0 - - let buttonInset: CGFloat = buttonTopInset + buttonHeight + buttonBottomInset - let buttonOffset = buttonInset + 10.0 - - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset - 500.0), size: CGSize(width: layout.size.width, height: buttonInset + 500.0))) + transition.updateFrame(node: self.topBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset - 500.0), size: CGSize(width: layout.size.width, height: buttonInset + 500.0))) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonInset - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))) transition.updateFrame(node: self.customColorItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonTopInset), size: colorLayout.contentSize)) @@ -334,7 +377,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { insets.top += spacing + buttonInset - self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: nil, lineSpacing: spacing, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in }) + self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: fillWidth, lineSpacing: spacing, itemSpacing: fillWidth != nil ? spacing : nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in }) self.gridNode.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) @@ -350,7 +393,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: GridNodeScrollToItem(index: 0, position: .top(0.0), transition: .animated(duration: 0.25, curve: .easeInOut), directionHint: .up, adjustForSection: true, adjustForTopInset: true), updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in }) - self.backgroundNode.layer.animatePosition(from: self.backgroundNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.backgroundNode.layer.position, duration: duration) + self.topBackgroundNode.layer.animatePosition(from: self.topBackgroundNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.topBackgroundNode.layer.position, duration: duration) self.separatorNode.layer.animatePosition(from: self.separatorNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.separatorNode.layer.position, duration: duration) self.customColorItemNode.layer.animatePosition(from: self.customColorItemNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.customColorItemNode.layer.position, duration: duration) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift index 852059bd5f..14658adb61 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift @@ -142,7 +142,7 @@ public final class ThemeGridController: ViewController { } }, presentGallery: { [weak self] in if let strongSelf = self { - let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, true)) + let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper)) controller.customSelection = { [weak self] asset in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c4ea359334..68f9c899af 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -13826,7 +13826,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.present(actionSheet, in: .window(.root)) } - private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, false), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) { + private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { return } @@ -18511,7 +18511,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let dismissControllers = { [weak self] in if let self, let navigationController = self.navigationController as? NavigationController { let controllers = navigationController.viewControllers.filter({ controller in - if controller is WallpaperGalleryController || controller is MediaPickerScreen { + if controller is WallpaperGalleryController || controller is AttachmentController { return false } return true @@ -18520,9 +18520,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, true)) - controller.navigationPresentation = .modal - controller.customSelection = { [weak self] asset in + let controller = standaloneMediaPickerController(context: strongSelf.context, subject: .assets(nil, .wallpaper), completion: { asset in guard let strongSelf = self else { return } @@ -18536,7 +18534,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } strongSelf.push(controller) - } + }) + controller.navigationPresentation = .flatModal strongSelf.push(controller) }, changeColor: { @@ -18547,8 +18546,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.themeScreen = nil themeController.dimTapped() } - let controller = ThemeColorsGridController(context: context, mode: .peer(EnginePeer(peer))) - controller.navigationPresentation = .modal + let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), push: { [weak self] controller in + if let strongSelf = self { + strongSelf.push(controller) + } + }) + controller.navigationPresentation = .flatModal strongSelf.push(controller) }, completion: { [weak self] emoticon in From 34062b0a06a4172597284b423349b806b53120f2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 6 Apr 2023 17:53:37 +0400 Subject: [PATCH 4/4] Chat wallpaper removal --- .../Sources/AttachmentPanel.swift | 33 +++++++-- .../Sources/MediaPickerScreen.swift | 69 +++++++++++++------ .../PremiumUI/Sources/PremiumGiftScreen.swift | 2 +- .../Themes/ThemeColorsGridController.swift | 15 +++- .../TelegramUI/Sources/ChatController.swift | 43 ++++++++---- .../WebUI/Sources/WebAppController.swift | 2 +- 6 files changed, 119 insertions(+), 45 deletions(-) diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index c6062f9f63..18b04c5803 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -387,7 +387,13 @@ public struct AttachmentMainButtonState { case center } + public enum Font: Equatable { + case regular + case bold + } + public let text: String? + public let font: Font public let background: Background public let textColor: UIColor public let isVisible: Bool @@ -396,6 +402,7 @@ public struct AttachmentMainButtonState { public init( text: String?, + font: Font, background: Background, textColor: UIColor, isVisible: Bool, @@ -403,6 +410,7 @@ public struct AttachmentMainButtonState { isEnabled: Bool ) { self.text = text + self.font = font self.background = background self.textColor = textColor self.isVisible = isVisible @@ -411,7 +419,7 @@ public struct AttachmentMainButtonState { } static var initial: AttachmentMainButtonState { - return AttachmentMainButtonState(text: nil, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false) + return AttachmentMainButtonState(text: nil, font: .bold, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false) } } @@ -643,7 +651,14 @@ private final class MainButtonNode: HighlightTrackingButtonNode { self.setupShimmering() if let text = state.text { - self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(17.0), textColor: state.textColor) + let font: UIFont + switch state.font { + case .regular: + font = Font.regular(17.0) + case .bold: + font = Font.semibold(17.0) + } + self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: state.textColor) let textSize = self.textNode.updateLayout(size) self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) @@ -1267,7 +1282,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { func updateMainButtonState(_ mainButtonState: AttachmentMainButtonState?) { var currentButtonState = self.mainButtonState if mainButtonState == nil { - currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled) + currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, font: currentButtonState.font, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled) } self.mainButtonState = mainButtonState ?? currentButtonState } @@ -1417,6 +1432,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.isUserInteractionEnabled = !isSelecting let isButtonVisible = self.mainButtonState.isVisible + let isNarrowButton = isButtonVisible && self.mainButtonState.font == .regular var insets = layout.insets(options: []) if let inputHeight = layout.inputHeight, inputHeight > 0.0 && (isSelecting || isButtonVisible) { @@ -1457,7 +1473,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { if isButtonVisible { var height: CGFloat if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero { - height = bounds.height + 9.0 + height = bounds.height if case .regular = layout.metrics.widthClass { if self.isStandalone { height -= 3.0 @@ -1466,7 +1482,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { } } } else { - height = bounds.height + 9.0 + 8.0 + height = bounds.height + 8.0 + } + if !isNarrowButton { + height += 9.0 } containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: height)) } else if isSelecting { @@ -1532,11 +1551,13 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { let sideInset: CGFloat = 16.0 let buttonSize = CGSize(width: layout.size.width - (sideInset + layout.safeInsets.left) * 2.0, height: 50.0) + let buttonTopInset: CGFloat = isNarrowButton ? 2.0 : 8.0 + if !self.dismissed { self.mainButtonNode.updateLayout(size: buttonSize, state: self.mainButtonState, transition: transition) } if !self.animatingTransition { - transition.updateFrame(node: self.mainButtonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + sideInset, y: isButtonVisible || self.fromMenu ? 8.0 : containerFrame.height), size: buttonSize)) + transition.updateFrame(node: self.mainButtonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + sideInset, y: isButtonVisible || self.fromMenu ? buttonTopInset : containerFrame.height), size: buttonSize)) } return containerFrame.height diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 7da26bf271..c06f852960 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -1238,7 +1238,24 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private var isDismissing = false - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, subject: Subject, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) { + fileprivate let mainButtonState: AttachmentMainButtonState? + private let mainButtonAction: (() -> Void)? + + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peer: EnginePeer?, + threadTitle: String?, + chatLocation: ChatLocation?, + bannedSendPhotos: (Int32, Bool)?, + bannedSendVideos: (Int32, Bool)?, + subject: Subject, + editingContext: TGMediaEditingContext? = nil, + selectionContext: TGMediaSelectionContext? = nil, + saveEditedPhotos: Bool = false, + mainButtonState: AttachmentMainButtonState? = nil, + mainButtonAction: (() -> Void)? = nil + ) { self.context = context let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } @@ -1251,6 +1268,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.bannedSendVideos = bannedSendVideos self.subject = subject self.saveEditedPhotos = saveEditedPhotos + self.mainButtonState = mainButtonState + self.mainButtonAction = mainButtonAction let selectionContext = selectionContext ?? TGMediaSelectionContext() @@ -1627,6 +1646,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } } + func mainButtonPressed() { + self.mainButtonAction?() + } + func dismissAllTooltips() { self.undoOverlayController?.dismissWithCommitAction() } @@ -1811,21 +1834,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } public var mediaPickerContext: AttachmentMediaPickerContext? { - if let interaction = self.interaction { - return MediaPickerContext(interaction: interaction) - } else { - return nil - } + return MediaPickerContext(controller: self) } } final class MediaPickerContext: AttachmentMediaPickerContext { - private weak var interaction: MediaPickerInteraction? + private weak var controller: MediaPickerScreen? var selectionCount: Signal { return Signal { [weak self] subscriber in - let disposable = self?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in - subscriber.putNext(Int(self?.interaction?.selectionState?.count() ?? 0)) + let disposable = self?.controller?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in + subscriber.putNext(Int(self?.controller?.interaction?.selectionState?.count() ?? 0)) }, error: { _ in }, completed: { }) return ActionDisposable { disposable?.dispose() @@ -1835,7 +1854,7 @@ final class MediaPickerContext: AttachmentMediaPickerContext { var caption: Signal { return Signal { [weak self] subscriber in - let disposable = self?.interaction?.editingState.forcedCaption().start(next: { caption in + let disposable = self?.controller?.interaction?.editingState.forcedCaption().start(next: { caption in if let caption = caption as? NSAttributedString { subscriber.putNext(caption) } else { @@ -1853,27 +1872,27 @@ final class MediaPickerContext: AttachmentMediaPickerContext { } public var mainButtonState: Signal { - return .single(nil) + return .single(self.controller?.mainButtonState) } - init(interaction: MediaPickerInteraction) { - self.interaction = interaction + init(controller: MediaPickerScreen) { + self.controller = controller } func setCaption(_ caption: NSAttributedString) { - self.interaction?.editingState.setForcedCaption(caption, skipUpdate: true) + self.controller?.interaction?.editingState.setForcedCaption(caption, skipUpdate: true) } func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { - self.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, {}) + self.controller?.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, {}) } func schedule() { - self.interaction?.schedule() + self.controller?.interaction?.schedule() } func mainButtonAction() { - + self.controller?.mainButtonPressed() } } @@ -1987,12 +2006,22 @@ public class MediaPickerGridSelectionGesture : UIPanGestureRecognizer { } } -public func standaloneMediaPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, subject: MediaPickerScreen.Subject, completion: @escaping (PHAsset) -> Void = { _ in }) -> ViewController { +public func wallpaperMediaPickerController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peer: EnginePeer, + canDelete: Bool, + completion: @escaping (PHAsset) -> Void = { _ in } +) -> ViewController { let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { return nil }) - controller.requestController = { _, present in - let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: subject) + controller.requestController = { [weak controller] _, present in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: canDelete ? AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true) : nil, mainButtonAction: canDelete ? { + let _ = context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start() + controller?.dismiss(animated: true) + } : nil) mediaPickerController.customSelection = completion present(mediaPickerController, mediaPickerController.mediaPickerContext) } diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index adf80c4f64..bfc61df365 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -645,7 +645,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent { price = nil } let buttonText = presentationData.strings.Premium_Gift_GiftSubscription(price ?? "—").string - self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true))) + self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, font: .bold, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true))) } func buy() { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift index 243de44ec9..a4b30a3610 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridController.swift @@ -150,7 +150,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina var pushController: (ViewController) -> Void = { _ in } - public init(context: AccountContext, mode: Mode = .default) { + public init(context: AccountContext, mode: Mode = .default, canDelete: Bool = false) { self.context = context self.mode = mode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -190,6 +190,10 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina self.pushController = { [weak self] controller in self?.push(controller) } + + if canDelete { + self.mainButtonStatePromise.set(.single(AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true))) + } } required public init(coder aDecoder: NSCoder) { @@ -317,7 +321,12 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina } @objc fileprivate func mainButtonPressed() { + guard case let .peer(peer) = self.mode else { + return + } + let _ = self.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start() + self.dismiss(animated: true) } public var requestAttachmentMenuExpansion: () -> Void = {} @@ -370,12 +379,12 @@ private final class ThemeColorsGridContext: AttachmentMediaPickerContext { } -public func standaloneColorPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, push: @escaping (ViewController) -> Void) -> ViewController { +public func standaloneColorPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, canDelete: Bool, push: @escaping (ViewController) -> Void) -> ViewController { let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { return nil }) controller.requestController = { _, present in - let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer)) + let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer), canDelete: canDelete) colorPickerController.pushController = { controller in push(controller) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 68f9c899af..bbd85374e4 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -18520,21 +18520,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - let controller = standaloneMediaPickerController(context: strongSelf.context, subject: .assets(nil, .wallpaper), completion: { asset in - guard let strongSelf = self else { - return - } - let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) - controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, options, cropRect in - if let strongSelf = self { - uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, peerId: peerId, completion: { - dismissControllers() - }) + var canDelete = false + if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData { + canDelete = cachedUserData.wallpaper != nil + } + let controller = wallpaperMediaPickerController( + context: strongSelf.context, + updatedPresentationData: strongSelf.updatedPresentationData, + peer: EnginePeer(peer), + canDelete: canDelete, + completion: { asset in + guard let strongSelf = self else { + return } + let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) + controller.navigationPresentation = .modal + controller.apply = { [weak self] wallpaper, options, cropRect in + if let strongSelf = self { + uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, peerId: peerId, completion: { + dismissControllers() + }) + } + } + strongSelf.push(controller) } - strongSelf.push(controller) - }) + ) controller.navigationPresentation = .flatModal strongSelf.push(controller) }, @@ -18546,7 +18556,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.themeScreen = nil themeController.dimTapped() } - let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), push: { [weak self] controller in + + var canDelete = false + if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData { + canDelete = cachedUserData.wallpaper != nil + } + let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), canDelete: canDelete, push: { [weak self] controller in if let strongSelf = self { strongSelf.push(controller) } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index af5b0191e1..e1b7eb7816 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -675,7 +675,7 @@ public final class WebAppController: ViewController, AttachmentContainable { let isLoading = json["is_progress_visible"] as? Bool let isEnabled = json["is_active"] as? Bool - let state = AttachmentMainButtonState(text: text, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .side : .none, isEnabled: isEnabled ?? true) + let state = AttachmentMainButtonState(text: text, font: .bold, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .side : .none, isEnabled: isEnabled ?? true) self.mainButtonState = state } }