From 6558d2e2651cb0ef1850bb7e9f195290890b7972 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 16 Jan 2026 21:23:32 +0800 Subject: [PATCH] Various improvements --- .bazelrc | 3 +- build-system/Make/Make.py | 7 - .../Sources/ContactsController.swift | 4 +- .../ContextUI/Sources/ContextController.swift | 11 ++ .../Sources/VoiceChatInfoContextItem.swift | 10 +- .../VoiceChatRecordingContextItem.swift | 51 ++---- .../VoiceChatShareScreenContextItem.swift | 23 +-- .../Sources/VoiceChatVolumeContextItem.swift | 4 +- .../ChatSendAsPeerTitleContextItem.swift | 6 +- .../Sources/ContextActionsContainerNode.swift | 147 +++++----------- .../ContextControllerActionsStackNode.swift | 162 ++++++++++++++---- .../EdgeEffect/Sources/EdgeEffect.swift | 12 +- .../Sources/GiftLoadingShimmerView.swift | 8 +- .../GiftAttributeListContextItem.swift | 28 ++- .../Sources/GiftStoreScreen.swift | 8 +- .../Sources/SearchContextItem.swift | 4 +- .../Sources/HorizontalTabsComponent.swift | 18 +- .../Sources/NavigationBarImpl.swift | 9 +- .../Sources/PeerInfoScreen.swift | 6 +- ...ScreenDisplayMediaGalleryContextMenu.swift | 4 +- .../Sources/PeerInfoSettingsTabActions.swift | 36 +--- .../Sources/SectionTitleContextItem.swift | 12 +- .../Sources/SliderContextItem.swift | 36 +++- .../Sources/ChatControllerNode.swift | 33 ++-- .../ChatHistoryNavigationButtonNode.swift | 1 + .../Sources/ContactSelectionController.swift | 11 -- 26 files changed, 332 insertions(+), 322 deletions(-) diff --git a/.bazelrc b/.bazelrc index 4471fa12d1..ee071c95e4 100644 --- a/.bazelrc +++ b/.bazelrc @@ -19,8 +19,7 @@ build --features=swift.enable_vfsoverlays build --strategy=Genrule=standalone build --spawn_strategy=standalone -build --strategy=SwiftCompile=standalone -build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1 +build --strategy=SwiftCompile=worker build:swift_profile --jobs=1 build:swift_profile --local_cpu_resources=1 diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py index 93b6a56da8..f6ffbfa0a3 100644 --- a/build-system/Make/Make.py +++ b/build-system/Make/Make.py @@ -82,13 +82,6 @@ class BazelCommandLine: ] self.common_debug_args = [ - # https://github.com/bazelbuild/rules_swift - # If enabled, Swift compilation actions will use batch mode by passing - # `-enable-batch-mode` to `swiftc`. This is a new compilation mode as of - # Swift 4.2 that is intended to speed up non-incremental non-WMO builds by - # invoking a smaller number of frontend processes and passing them batches of - # source files. - '--features=swift.enable_batch_mode', ] self.common_release_args = [ diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index af363a56a2..d69affeae4 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -626,11 +626,11 @@ public class ContactsController: ViewController { } var items: [ContextMenuItem] = [] - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByLastSeen, icon: { theme in return currentSettings.sortOrder == .presence ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByLastSeen, icon: { theme in return currentSettings.sortOrder == .presence ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in f(.default) updateSortOrder(.presence) }))) - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByName, icon: { theme in return currentSettings.sortOrder == .natural ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByName, icon: { theme in return currentSettings.sortOrder == .natural ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in f(.default) updateSortOrder(.natural) }))) diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index a82ee38cdb..9b4ff1f5c1 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -129,6 +129,7 @@ public final class ContextMenuActionItem { public let textColor: ContextMenuActionItemTextColor public let textFont: ContextMenuActionItemFont public let textLayout: ContextMenuActionItemTextLayout + public let customTextInsets: UIEdgeInsets? public let parseMarkdown: Bool public let badge: ContextMenuActionBadge? public let icon: (PresentationTheme) -> UIImage? @@ -150,6 +151,7 @@ public final class ContextMenuActionItem { enableEntityAnimations: Bool = true, textColor: ContextMenuActionItemTextColor = .primary, textLayout: ContextMenuActionItemTextLayout = .twoLinesMax, + customTextInsets: UIEdgeInsets? = nil, textFont: ContextMenuActionItemFont = .regular, parseMarkdown: Bool = false, badge: ContextMenuActionBadge? = nil, @@ -172,6 +174,7 @@ public final class ContextMenuActionItem { enableEntityAnimations: enableEntityAnimations, textColor: textColor, textLayout: textLayout, + customTextInsets: customTextInsets, textFont: textFont, parseMarkdown: parseMarkdown, badge: badge, @@ -204,6 +207,7 @@ public final class ContextMenuActionItem { enableEntityAnimations: Bool = true, textColor: ContextMenuActionItemTextColor = .primary, textLayout: ContextMenuActionItemTextLayout = .twoLinesMax, + customTextInsets: UIEdgeInsets? = nil, textFont: ContextMenuActionItemFont = .regular, parseMarkdown: Bool = false, badge: ContextMenuActionBadge? = nil, @@ -226,6 +230,7 @@ public final class ContextMenuActionItem { self.textColor = textColor self.textFont = textFont self.textLayout = textLayout + self.customTextInsets = customTextInsets self.parseMarkdown = parseMarkdown self.badge = badge self.icon = icon @@ -250,12 +255,17 @@ public protocol ContextMenuCustomNode: ASDisplayNode { func performAction() var needsSeparator: Bool { get } + var needsPadding: Bool { get } } public extension ContextMenuCustomNode { var needsSeparator: Bool { return true } + + var needsPadding: Bool { + return true + } } public protocol ContextMenuCustomItem { @@ -798,6 +808,7 @@ public protocol ContextControllerActionsStackItemNode: ASDisplayNode { transition: ContainedViewLayoutTransition ) -> (size: CGSize, apparentHeight: CGFloat) + func highlightGestureShouldBegin(location: CGPoint) -> Bool func highlightGestureMoved(location: CGPoint) func highlightGestureFinished(performAction: Bool) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatInfoContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatInfoContextItem.swift index 1354709d5b..b9bf82a190 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatInfoContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatInfoContextItem.swift @@ -64,9 +64,9 @@ private final class VoiceChatInfoContextItemNode: ASDisplayNode, ContextMenuCust } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 12.0 - let verticalInset: CGFloat = 12.0 + let sideInset: CGFloat = 18.0 + let iconSideInset: CGFloat = 20.0 + let verticalInset: CGFloat = 11.0 let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize() @@ -80,11 +80,11 @@ private final class VoiceChatInfoContextItemNode: ASDisplayNode, ContextMenuCust return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in let verticalOrigin = floor((size.height - textSize.height) / 2.0) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) if !iconSize.width.isZero { - transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) + transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) } transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatRecordingContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatRecordingContextItem.swift index 3d90146154..2620fe3606 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatRecordingContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatRecordingContextItem.swift @@ -98,7 +98,6 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen private let actionSelected: (ContextMenuActionResult) -> Void private let backgroundNode: ASDisplayNode - private let highlightedBackgroundNode: ASDisplayNode private let textNode: ImmediateTextNode private let statusNode: ImmediateTextNode private let iconNode: VoiceChatRecordingIconNode @@ -120,10 +119,6 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen self.backgroundNode = ASDisplayNode() self.backgroundNode.isAccessibilityElement = false self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor - self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isAccessibilityElement = false - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - self.highlightedBackgroundNode.alpha = 0.0 self.textNode = ImmediateTextNode() self.textNode.isAccessibilityElement = false @@ -149,23 +144,11 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen super.init() self.addSubnode(self.backgroundNode) - self.addSubnode(self.highlightedBackgroundNode) self.addSubnode(self.textNode) self.addSubnode(self.statusNode) self.addSubnode(self.iconNode) self.addSubnode(self.buttonNode) - self.buttonNode.highligthedChanged = { [weak self] highligted in - guard let strongSelf = self else { - return - } - if highligted { - strongSelf.highlightedBackgroundNode.alpha = 1.0 - } else { - strongSelf.highlightedBackgroundNode.alpha = 0.0 - strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - } self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } @@ -176,14 +159,8 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen override func didLoad() { super.didLoad() - self.pointerInteraction = PointerInteraction(node: self.buttonNode, style: .hover, willEnter: { [weak self] in - if let strongSelf = self { - strongSelf.highlightedBackgroundNode.alpha = 0.75 - } - }, willExit: { [weak self] in - if let strongSelf = self { - strongSelf.highlightedBackgroundNode.alpha = 0.0 - } + self.pointerInteraction = PointerInteraction(node: self.buttonNode, style: .hover, willEnter: { + }, willExit: { }) let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in @@ -205,15 +182,16 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen let subtextFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0) self.statusNode.attributedText = NSAttributedString(string: stringForDuration(Int32(duration)), font: subtextFont, textColor: presentationData.theme.contextMenu.secondaryColor) - let sideInset: CGFloat = 16.0 + let sideInset: CGFloat = 18.0 + let iconSideInset: CGFloat = 22.0 let statusSize = self.statusNode.updateLayout(CGSize(width: size.width - sideInset - 32.0, height: .greatestFiniteMagnitude)) - transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: sideInset, y: self.statusNode.frame.minY), size: statusSize)) + transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: self.statusNode.frame.minY), size: statusSize)) } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 12.0 - let verticalInset: CGFloat = 12.0 + let sideInset: CGFloat = 18.0 + let iconSideInset: CGFloat = 22.0 + let verticalInset: CGFloat = 11.0 let iconSide = 16.0 + (1.0 + UIScreenPixel) * 2.0 let iconSize: CGSize = CGSize(width: iconSide, height: iconSide) @@ -237,23 +215,21 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen self.updateTime(transition: .immediate) } let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: verticalOrigin), size: textSize) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) - transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin + verticalSpacing + textSize.height), size: textSize)) + transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: verticalOrigin + verticalSpacing + textSize.height), size: textSize)) if !iconSize.width.isZero { - transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) + transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) } transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) - transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) }) } func updateTheme(presentationData: PresentationData) { self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize) let subtextFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0) @@ -284,10 +260,5 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen } func setIsHighlighted(_ value: Bool) { - if value { - self.highlightedBackgroundNode.alpha = 1.0 - } else { - self.highlightedBackgroundNode.alpha = 0.0 - } } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift index d3227b10b1..e9f60b2bd7 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift @@ -37,7 +37,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM private let actionSelected: (ContextMenuActionResult) -> Void private let backgroundNode: ASDisplayNode - private let highlightedBackgroundNode: ASDisplayNode private let textNode: ImmediateTextNode private let iconNode: ASImageNode @@ -59,10 +58,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM self.backgroundNode = ASDisplayNode() self.backgroundNode.isAccessibilityElement = false self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor - self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isAccessibilityElement = false - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - self.highlightedBackgroundNode.alpha = 0.0 self.textNode = ImmediateTextNode() self.textNode.isAccessibilityElement = false @@ -89,7 +84,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM super.init() self.addSubnode(self.backgroundNode) - self.addSubnode(self.highlightedBackgroundNode) if let broadcastPickerView = self.broadcastPickerView { self.view.addSubview(broadcastPickerView) } @@ -118,9 +112,9 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM private var validLayout: CGSize? func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 12.0 - let verticalInset: CGFloat = 12.0 + let sideInset: CGFloat = 18.0 + let iconSideInset: CGFloat = 20.0 + let verticalInset: CGFloat = 11.0 let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize() @@ -139,15 +133,14 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM self.validLayout = size let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) if !iconSize.width.isZero { - transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) + transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) } transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) - transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) if let broadcastPickerView = self.broadcastPickerView { broadcastPickerView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) @@ -157,7 +150,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM func updateTheme(presentationData: PresentationData) { self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize) @@ -186,10 +178,5 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM } func setIsHighlighted(_ value: Bool) { - if value { - self.highlightedBackgroundNode.alpha = 1.0 - } else { - self.highlightedBackgroundNode.alpha = 0.0 - } } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatVolumeContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatVolumeContextItem.swift index 69aa80c1b8..47c00293f2 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatVolumeContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatVolumeContextItem.swift @@ -41,6 +41,8 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu } } + let needsPadding: Bool = false + private let valueChanged: (CGFloat, Bool) -> Void private let hapticFeedback = HapticFeedback() @@ -134,7 +136,7 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu textSize.width = valueWidth return (CGSize(width: height * 3.0, height: height), { size, transition in - let leftInset: CGFloat = 17.0 + let leftInset: CGFloat = 25.0 let textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - textSize.height) / 2.0)), size: textSize) transition.updateFrameAdditive(node: self.backgroundTextNode, frame: textFrame) diff --git a/submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu/Sources/ChatSendAsPeerTitleContextItem.swift b/submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu/Sources/ChatSendAsPeerTitleContextItem.swift index b8f843f98a..d8868d5ab1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu/Sources/ChatSendAsPeerTitleContextItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu/Sources/ChatSendAsPeerTitleContextItem.swift @@ -29,6 +29,8 @@ private final class ChatSendAsPeerTitleContextItemNode: ASDisplayNode, ContextMe private let backgroundNode: ASDisplayNode private let textNode: ImmediateTextNode + let needsPadding: Bool = false + init(presentationData: PresentationData, item: ChatSendAsPeerTitleContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { self.item = item self.presentationData = presentationData @@ -55,10 +57,10 @@ private final class ChatSendAsPeerTitleContextItemNode: ASDisplayNode, ContextMe } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 + let sideInset: CGFloat = 18.0 let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - sideInset, height: .greatestFiniteMagnitude)) - let height: CGFloat = 28.0 + let height: CGFloat = 28.0 + 10.0 return (CGSize(width: textSize.width + sideInset + sideInset, height: height), { size, transition in let verticalOrigin = floor((size.height - textSize.height) / 2.0) diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift index 2602854638..758293151a 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift @@ -11,6 +11,9 @@ import TextFormat import TextNodeWithEntities import SwiftSignalKit import ContextUI +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters private final class ContextActionsSelectionGestureRecognizer: UIPanGestureRecognizer { var updateLocation: ((CGPoint, Bool) -> Void)? @@ -325,10 +328,7 @@ private final class InnerActionsContainerNode: ASDisplayNode { final class InnerTextSelectionTipContainerNode: ASDisplayNode { private let presentationData: PresentationData - let shadowNode: ASImageNode - private let backgroundNode: ASDisplayNode - private var effectView: UIVisualEffectView? - private let highlightBackgroundNode: ASDisplayNode + private var background: (container: GlassBackgroundContainerView, background: GlassBackgroundView)? private let buttonNode: HighlightTrackingButtonNode private let textNode: TextNodeWithEntities private var textSelectionNode: TextSelectionNode? @@ -347,22 +347,15 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { private var action: (() -> Void)? var requestDismiss: (@escaping () -> Void) -> Void = { _ in } - init(presentationData: PresentationData, tip: ContextController.Tip) { + init(presentationData: PresentationData, tip: ContextController.Tip, isInline: Bool) { self.tip = tip self.presentationData = presentationData - self.shadowNode = ASImageNode() - self.shadowNode.displaysAsynchronously = false - self.shadowNode.displayWithoutProcessing = true - self.shadowNode.image = UIImage(bundleImageName: "Components/Context Menu/Shadow")?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 60) - self.shadowNode.contentMode = .scaleToFill - self.shadowNode.isHidden = true - - self.backgroundNode = ASDisplayNode() - - self.highlightBackgroundNode = ASDisplayNode() - self.highlightBackgroundNode.isAccessibilityElement = false - self.highlightBackgroundNode.alpha = 0.0 + if !isInline { + self.background = (GlassBackgroundContainerView(), GlassBackgroundView()) + } else { + self.background = nil + } self.buttonNode = HighlightTrackingButtonNode() @@ -448,13 +441,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { super.init() - self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor - self.backgroundNode.clipsToBounds = true - self.backgroundNode.cornerRadius = 14.0 - - self.highlightBackgroundNode.clipsToBounds = true - self.highlightBackgroundNode.cornerRadius = 14.0 - let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: presentationData.theme.contextMenu.primaryColor.withAlphaComponent(0.15), knob: presentationData.theme.contextMenu.primaryColor, knobDiameter: 8.0, isDark: presentationData.theme.overallDarkAppearance), strings: presentationData.strings, textNode: self.textNode.textNode, updateIsActive: { _ in }, present: { _, _ in }, rootNode: { [weak self] in @@ -463,17 +449,24 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { }) self.textSelectionNode = textSelectionNode - self.addSubnode(self.backgroundNode) - self.addSubnode(self.highlightBackgroundNode) - self.addSubnode(self.textNode.textNode) - self.addSubnode(self.iconNode) - self.addSubnode(self.placeholderNode) + let parentView: UIView + if let background = self.background { + self.view.addSubview(background.container) + background.container.contentView.addSubview(background.background) + parentView = background.background.contentView + } else { + parentView = self.view + } - self.textSelectionNode.flatMap(self.addSubnode) + parentView.addSubview(self.textNode.textNode.view) + parentView.addSubview(self.iconNode.view) + parentView.addSubview(self.placeholderNode.view) - self.addSubnode(textSelectionNode.highlightAreaNode) + self.textSelectionNode.flatMap { parentView.addSubview($0.view) } - self.addSubnode(self.buttonNode) + parentView.addSubview(textSelectionNode.highlightAreaNode.view) + + parentView.addSubview(self.buttonNode.view) self.buttonNode.highligthedChanged = { [weak self] highlighted in guard let strongSelf = self else { @@ -512,7 +505,11 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { ] for node in nodes { - other.addSubnode(node) + if let background = other.background { + background.background.contentView.addSubview(node.view) + } else { + other.view.addSubview(node.view) + } node.layer.animateAlpha(from: node.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in node?.removeFromSupernode() }) @@ -531,44 +528,13 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { } func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { - var needsBlur = false - if case .regular = widthClass { - needsBlur = true - } else if case .inline = presentation { - needsBlur = true - } - - if !needsBlur { - if let effectView = self.effectView { - self.effectView = nil - effectView.removeFromSuperview() - } - } else { - if self.effectView == nil { - let effectView: UIVisualEffectView - if #available(iOS 13.0, *) { - if self.presentationData.theme.overallDarkAppearance { - effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark)) - } else { - effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight)) - } - } else { - effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - } - effectView.clipsToBounds = true - effectView.layer.cornerRadius = self.backgroundNode.cornerRadius - self.effectView = effectView - self.view.insertSubview(effectView, at: 0) - } - } - - let verticalInset: CGFloat = 10.0 - let horizontalInset: CGFloat = 16.0 + let verticalInset: CGFloat = 16.0 + let horizontalInset: CGFloat = 18.0 let standardIconWidth: CGFloat = 32.0 - let iconSideInset: CGFloat = 12.0 + let iconSideInset: CGFloat = 20.0 - let textFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)) - let boldTextFont = Font.bold(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)) + let textFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)) + let boldTextFont = Font.bold(floor(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)) let textColor = self.presentationData.theme.contextMenu.primaryColor let linkColor = self.presentationData.theme.overallDarkAppearance ? UIColor(rgb: 0x64d2ff) : self.presentationData.theme.contextMenu.badgeFillColor @@ -626,7 +592,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: horizontalInset, y: floorToScreenPixels((size.height - lineHeight) / 2.0)), size: CGSize(width: width - horizontalInset * 2.0, height: lineHeight))) transition.updateAlpha(node: self.placeholderNode, alpha: textFrame.height.isZero ? 1.0 : 0.0) - let iconFrame = CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize) + let iconFrame = CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize) transition.updateFrame(node: self.iconNode, frame: iconFrame) if let textSelectionNode = self.textSelectionNode { @@ -634,30 +600,24 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { textSelectionNode.highlightAreaNode.frame = textFrame } - switch presentation { - case .modal: - self.shadowNode.isHidden = true - case .inline, .additional: - self.shadowNode.isHidden = false - } - - if let effectView = self.effectView { - transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size)) - } - - self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - return size } func setActualSize(size: CGSize, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) + let transition = ComponentTransition(transition) + + if let background = self.background { + background.container.update(size: size, isDark: self.presentationData.theme.overallDarkAppearance, transition: transition) + transition.setFrame(view: background.container, frame: CGRect(origin: CGPoint(), size: size)) + + background.background.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition) + transition.setFrame(view: background.background, frame: CGRect(origin: CGPoint(), size: size)) + } + self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) } func updateTheme(presentationData: PresentationData) { - self.backgroundColor = presentationData.theme.contextMenu.backgroundColor } func animateIn() { @@ -673,17 +633,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { } func updateHighlight(animated: Bool) { - if self.isButtonHighlighted || self.isHighlighted { - self.highlightBackgroundNode.alpha = 1.0 - } else { - if animated { - let previousAlpha = self.highlightBackgroundNode.alpha - self.highlightBackgroundNode.alpha = 0.0 - self.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2) - } else { - self.highlightBackgroundNode.alpha = 0.0 - } - } } private var isButtonHighlighted = false @@ -796,8 +745,6 @@ final class ContextActionsContainerNode: ASDisplayNode { super.init() - //self.addSubnode(self.shadowNode) - //self.additionalShadowNode.flatMap(self.addSubnode) self.additionalActionsNode.flatMap(self.scrollNode.addSubnode) self.scrollNode.addSubnode(self.actionsNode) self.addSubnode(self.scrollNode) @@ -857,7 +804,7 @@ final class ContextActionsContainerNode: ASDisplayNode { textSelectionTipNode.removeFromSupernode() } - let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: self.presentationData, tip: tip) + let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: self.presentationData, tip: tip, isInline: false) let getController = self.getController textSelectionTipNode.requestDismiss = { completion in getController()?.dismiss(completion: completion) diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift index 68ce078274..230afd4b51 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift @@ -26,6 +26,8 @@ public protocol ContextControllerActionsListItemNode: ASDisplayNode { func canBeHighlighted() -> Bool func updateIsHighlighted(isHighlighted: Bool) func performAction() + + var needsPadding: Bool { get } } public final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode { @@ -49,6 +51,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking private var iconDisposable: Disposable? + public let needsPadding: Bool = true + public init( context: AccountContext?, getController: @escaping () -> ContextControllerProtocol?, @@ -205,10 +209,10 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking let sideInset: CGFloat = 18.0 let verticalInset: CGFloat = 11.0 let titleSubtitleSpacing: CGFloat = 1.0 - let iconSideInset: CGFloat = 18.0 + 6.0 + let iconSideInset: CGFloat = 20.0 let standardIconWidth: CGFloat = 32.0 let iconSpacing: CGFloat = 8.0 - + var forcedHeight: CGFloat? var titleVerticalOffset: CGFloat? let titleFont: UIFont @@ -490,6 +494,10 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking maxTextWidth -= sideInset } + if let additionalIconSize { + maxTextWidth -= additionalIconSize.width + } + if let badgeSize = badgeSize { maxTextWidth -= badgeSize.width maxTextWidth -= 8.0 @@ -510,8 +518,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking } else { minSize.width += sideInset } - if self.item.additionalLeftIcon != nil { - minSize.width += 24.0 + if let additionalIconSize { + minSize.width += additionalIconSize.width minSize.width += iconSideInset minSize.width += iconSpacing } @@ -528,13 +536,15 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking return (minSize: minSize, apply: { size, transition in var titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize) + if let customTextInsets = self.item.customTextInsets { + titleFrame.origin.x = customTextInsets.left + } if let titleVerticalOffset { titleFrame = titleFrame.offsetBy(dx: 0.0, dy: titleVerticalOffset) } var subtitleFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize) - if self.item.additionalLeftIcon != nil { - } else if iconSize != nil { - titleFrame.origin.x = iconSideInset + 36.0 + if iconSize != nil { + titleFrame.origin.x = iconSideInset + 40.0 subtitleFrame.origin.x = titleFrame.minX } @@ -548,7 +558,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking if let iconSize { let iconFrame = CGRect( origin: CGPoint( - x: iconSideInset, + x: iconSideInset + floor((standardIconWidth - iconSize.width) * 0.5), y: floor((size.height - iconSize.height) / 2.0) ), size: iconSize @@ -577,7 +587,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking if let additionalIconSize { let iconFrame = CGRect( origin: CGPoint( - x: 10.0, + x: size.width - iconSideInset - additionalIconSize.width, y: floor((size.height - additionalIconSize.height) / 2.0) ), size: additionalIconSize @@ -591,6 +601,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking private final class ContextControllerActionsListSeparatorItemNode: ASDisplayNode, ContextControllerActionsListItemNode { private let separatorView: UIImageView + let needsPadding: Bool = false + func canBeHighlighted() -> Bool { return false } @@ -644,6 +656,14 @@ private final class ContextControllerActionsListCustomItemNode: ASDisplayNode, C } } + var needsPadding: Bool { + if let itemNode = self.itemNode { + return itemNode.needsPadding + } else { + return true + } + } + private let getController: () -> ContextControllerProtocol? private let item: ContextMenuCustomItem private let requestDismiss: (ContextMenuActionResult) -> Void @@ -733,6 +753,10 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio private var items: [ContextMenuItem] private var itemNodes: [Item] + private var tip: ContextController.Tip? + private var tipDisposable: Disposable? + private var tipNode: InnerTextSelectionTipContainerNode? + private var hapticFeedback: HapticFeedback? private let highlightedItemBackgroundView: UIView @@ -750,13 +774,16 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio getController: @escaping () -> ContextControllerProtocol?, requestDismiss: @escaping (ContextMenuActionResult) -> Void, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, - items: [ContextMenuItem] + items: [ContextMenuItem], + tip: ContextController.Tip?, + tipSignal: Signal? ) { self.context = context self.requestUpdate = requestUpdate self.getController = getController self.requestDismiss = requestDismiss self.items = items + self.tip = tip var requestUpdateAction: ((AnyHashable, ContextMenuActionItem) -> Void)? self.itemNodes = items.map { item -> Item in @@ -807,6 +834,10 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio } } + deinit { + self.tipDisposable?.dispose() + } + func updateItems(items: [ContextMenuItem]) { self.items = items for i in 0 ..< items.count { @@ -890,21 +921,35 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio let verticalInset: CGFloat = 10.0 var itemNodeLayouts: [(minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void)] = [] - var combinedSize = CGSize(width: 0.0, height: verticalInset) - for item in self.itemNodes { + var combinedSize = CGSize(width: 0.0, height: 0.0) + for i in 0 ..< self.itemNodes.count { + let item = self.itemNodes[i] + let itemNodeLayout = item.node.update( presentationData: presentationData, constrainedSize: CGSize(width: standardMaxWidth, height: constrainedSize.height) ) + + if item.node.needsPadding { + if i == 0 { + combinedSize.height += verticalInset + } + } + itemNodeLayouts.append(itemNodeLayout) combinedSize.width = max(combinedSize.width, itemNodeLayout.minSize.width) combinedSize.height += itemNodeLayout.minSize.height + + if item.node.needsPadding { + if i == self.itemNodes.count - 1 { + combinedSize.height += verticalInset + } + } } - combinedSize.height += verticalInset self.invalidatedItemNodes = false combinedSize.width = max(combinedSize.width, standardMinWidth) - var nextItemOrigin = CGPoint(x: 0.0, y: verticalInset) + var nextItemOrigin = CGPoint(x: 0.0, y: 0.0) for i in 0 ..< self.itemNodes.count { let item = self.itemNodes[i] let itemNodeLayout = itemNodeLayouts[i] @@ -914,6 +959,12 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio itemTransition = .immediate } + if item.node.needsPadding { + if i == 0 { + nextItemOrigin.y += verticalInset + } + } + let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height) let itemFrame = CGRect(origin: nextItemOrigin, size: itemSize) itemTransition.updateFrame(node: item.node, frame: itemFrame, beginWithCurrentState: true) @@ -926,6 +977,27 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio } } + if let tip = self.tip, !"".isEmpty { + let tipNode: InnerTextSelectionTipContainerNode + var tipTransition = transition + if let current = self.tipNode { + tipNode = current + } else { + tipTransition = .immediate + tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: true) + self.addSubnode(tipNode) + self.tipNode = tipNode + } + let tipSize = tipNode.updateLayout(widthClass: .compact, presentation: .inline, width: combinedSize.width, transition: tipTransition) + let tipFrame = CGRect(origin: nextItemOrigin, size: tipSize) + tipTransition.updateFrame(node: tipNode, frame: tipFrame) + nextItemOrigin.y += tipSize.height + combinedSize.height += tipSize.height + } else if let tipNode = self.tipNode { + tipNode.removeFromSupernode() + self.tipNode = nil + } + if let highlightedItemFrame { self.highlightedItemBackgroundView.backgroundColor = presentationData.theme.overallDarkAppearance ? UIColor.white : UIColor.black self.highlightedItemBackgroundView.setMonochromaticEffect(tintColor: self.highlightedItemBackgroundView.backgroundColor) @@ -955,6 +1027,18 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio return (combinedSize, combinedSize.height) } + func highlightGestureShouldBegin(location: CGPoint) -> Bool { + for itemNode in self.itemNodes { + if itemNode.node.frame.contains(location) { + if !itemNode.node.canBeHighlighted() { + return false + } + break + } + } + return true + } + func highlightGestureMoved(location: CGPoint) { var highlightedItemNode: Item? for itemNode in self.itemNodes { @@ -1058,7 +1142,9 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio getController: getController, requestDismiss: requestDismiss, requestUpdate: requestUpdate, - items: self.items + items: self.items, + tip: self.tip, + tipSignal: self.tipSignal ) } } @@ -1110,6 +1196,10 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta return (contentLayout.cleanSize, contentLayout.apparentHeight) } + func highlightGestureShouldBegin(location: CGPoint) -> Bool { + return true + } + func highlightGestureMoved(location: CGPoint) { } @@ -1195,7 +1285,7 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> [C } private final class ItemSelectionRecognizer: UIGestureRecognizer { - var shouldBegin: (() -> Bool)? + var shouldBegin: ((CGPoint) -> Bool)? private var initialLocation: CGPoint? private var currentLocation: CGPoint? @@ -1216,7 +1306,7 @@ private final class ItemSelectionRecognizer: UIGestureRecognizer { public override func touchesBegan(_ touches: Set, with event: UIEvent) { super.touchesBegan(touches, with: event) - if let shouldBegin = self.shouldBegin, !shouldBegin() { + if let location = touches.first?.location(in: self.view), let shouldBegin = self.shouldBegin, !shouldBegin(location) { self.state = .failed return } @@ -1283,7 +1373,6 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context self.contentContainer = UIView() self.contentContainer.clipsToBounds = true - self.contentContainer.layer.cornerRadius = 30.0 self.backgroundView.contentView.addSubview(self.contentContainer) super.init() @@ -1416,12 +1505,14 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context }) } + transition.setCornerRadius(layer: self.contentContainer.layer, cornerRadius: min(30.0, size.height * 0.5)) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) - self.backgroundView.update(size: size, cornerRadius: 30.0, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + self.backgroundView.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) if let sourceExtractableContainer = self.sourceExtractableContainer { transition.setFrame(view: sourceExtractableContainer.extractableContentView, frame: CGRect(origin: CGPoint(), size: size)) - sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: 30.0, state: .animatedIn), transition: transition.containedViewLayoutTransition) + sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: min(30.0, size.height * 0.5), state: .animatedIn), transition: transition.containedViewLayoutTransition) } } } @@ -1540,7 +1631,7 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context } else { let previousTipNode = self.tipNode updatedTransition = .immediate - let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip) + let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: false) tipNode.requestDismiss = { [weak self] completion in self?.getController()?.dismiss(completion: completion) } @@ -1572,6 +1663,10 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction, beginWithCurrentState: true) } + func highlightGestureShouldBegin(at location: CGPoint) -> Bool { + return self.node.highlightGestureShouldBegin(location: self.view.convert(location, to: self.node.view)) + } + func highlightGestureMoved(location: CGPoint) { if let tipNode = self.tipNode { let tipLocation = self.view.convert(location, to: tipNode.view) @@ -1655,7 +1750,7 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context } let selectionPanGesture = ItemSelectionRecognizer(target: self, action: #selector(self.panGesture(_:))) - selectionPanGesture.shouldBegin = { [weak self] in + selectionPanGesture.shouldBegin = { [weak self] point in guard let self, let topItemContainer = self.itemContainers.last else { return false } @@ -1663,6 +1758,9 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context if topItemContainer.item is ContextControllerActionsCustomStackItem { return false } + if !topItemContainer.highlightGestureShouldBegin(at: self.view.convert(point, to: topItemContainer.view)) { + return false + } return true } @@ -1959,32 +2057,25 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context let tipTransition = transition var animateTipIn = false if tip.tipNode.supernode == nil { - self.insertSubnode(tip.tipNode.shadowNode, at: 0) self.addSubnode(tip.tipNode) animateTipIn = transition.isAnimated let tipFrame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)) tip.tipNode.frame = tipFrame tip.tipNode.setActualSize(size: tipFrame.size, transition: .immediate) - transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0)) } let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)) tipTransition.updateFrame(node: tip.tipNode, frame: tipFrame, beginWithCurrentState: true) - transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0)) tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: tipTransition) if animateTipIn { - tip.tipNode.alpha = tipAlpha - tip.tipNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - - tip.tipNode.shadowNode.alpha = tipAlpha - tip.tipNode.shadowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + tip.tipNode.alpha = 0.0 + ComponentTransition.easeInOut(duration: 0.2).setAlpha(view: tip.tipNode.view, alpha: 1.0) } else { - tipTransition.updateAlpha(node: tip.tipNode, alpha: tipAlpha, beginWithCurrentState: true) - tipTransition.updateAlpha(node: tip.tipNode.shadowNode, alpha: tipAlpha, beginWithCurrentState: true) + ComponentTransition(tipTransition).setAlpha(view: tip.tipNode.view, alpha: tipAlpha) } if i == self.itemContainers.count - 1 { @@ -2006,15 +2097,10 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context if let tipNode = itemContainer.tipNode { let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: tipNode.frame.size) transition.updateFrame(node: tipNode, frame: tipFrame, beginWithCurrentState: true) - transition.updateFrame(node: tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0)) - transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in + ComponentTransition(transition).setAlpha(view: tipNode.view, alpha: 0.0, completion: { [weak tipNode] _ in tipNode?.removeFromSupernode() }) - let shadowNode = tipNode.shadowNode - transition.updateAlpha(node: shadowNode, alpha: 0.0, completion: { [weak shadowNode] _ in - shadowNode?.removeFromSupernode() - }) } } self.dismissingItemContainers.removeAll() diff --git a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift index b1ef32302c..8b2bd9204c 100644 --- a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift +++ b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift @@ -51,6 +51,9 @@ public class EdgeEffectView: UIView { } if blur { + let blurHeight: CGFloat = max(edgeSize, bounds.height - 14.0) + let blurFrame = CGRect(origin: CGPoint(x: 0.0, y: edge == .bottom ? (bounds.height - blurHeight) : 0.0), size: CGSize(width: bounds.width, height: blurHeight)) + let blurView: VariableBlurView if let current = self.blurView { blurView = current @@ -59,14 +62,15 @@ public class EdgeEffectView: UIView { self.insertSubview(blurView, at: 0) self.blurView = blurView } + blurView.update( - size: bounds.size, - constantHeight: edgeSize, + size: blurFrame.size, + constantHeight: max(1.0, edgeSize - 4.0), isInverted: edge == .bottom, - gradient: EdgeEffectView.generateEdgeGradientData(baseHeight: edgeSize), + gradient: EdgeEffectView.generateEdgeGradientData(baseHeight: max(1.0, edgeSize - 4.0)), transition: transition.containedViewLayoutTransition ) - transition.setFrame(view: blurView, frame: bounds) + transition.setFrame(view: blurView, frame: blurFrame) blurView.transform = self.contentMaskView.transform } else if let blurView = self.blurView { self.blurView = nil diff --git a/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift b/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift index 07ecd244b9..c6a1e80f69 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift @@ -159,12 +159,12 @@ public final class GiftLoadingShimmerView: UIView { let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0 for i in 0 ..< 4 { let rect = CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0), - size: CGSize(width: filterWidth, height: 28.0)) - context.addPath(CGPath(roundedRect: rect, cornerWidth: 14.0, cornerHeight: 14.0, transform: nil)) + size: CGSize(width: filterWidth, height: 36.0)) + context.addPath(CGPath(roundedRect: rect, cornerWidth: 36.0 * 0.5, cornerHeight: 36.0 * 0.5, transform: nil)) } } - var currentY: CGFloat = 52.0 + 7.0 + var currentY: CGFloat = 52.0 + 16.0 var rowIndex: Int = 0 let optionSpacing: CGFloat = 10.0 @@ -178,7 +178,7 @@ public final class GiftLoadingShimmerView: UIView { for i in 0 ..< 3 { let itemOrigin = CGPoint( x: sideInset + CGFloat(i) * (itemSize.width + optionSpacing), - y: 39.0 + 9.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing) + y: 39.0 + 13.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing) ) context.addPath(CGPath(roundedRect: CGRect(origin: itemOrigin, size: itemSize), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil)) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift index 726eeca4e8..0f46e875b1 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift @@ -83,7 +83,6 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen ) title += count } - let words = title.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty } var wordStartIndices: [String.Index] = [] @@ -118,7 +117,9 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen } } - return ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, parseMarkdown: true, icon: { theme in + return ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, customTextInsets: UIEdgeInsets(top: 0.0, left: 18.0 + 5.0, bottom: 0.0, right: 0.0), parseMarkdown: true, icon: { _ in + return nil + }, additionalLeftIcon: { theme in return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in getController()?.dismiss(result: .dismissWithoutContent, completion: nil) @@ -134,7 +135,7 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen let isSelected = selectedAttributes.isEmpty || selectedAttributes.contains(attributeId) var entities: [MessageTextEntity] = [] - var title = " \(name)" + var title = "\(name)" var count = "" if let counter = item.attributeCount[attributeId] { count = " \(presentationStringsFormattedNumber(counter, presentationData.dateTimeFormat.groupingSeparator))" @@ -177,10 +178,10 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen } } - return ContextMenuActionItem(text: title, entities: entities, icon: { theme in - return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, additionalLeftIcon: { _ in + return ContextMenuActionItem(text: title, entities: entities, icon: { _ in return generateGradientFilledCircleImage(diameter: 24.0, colors: [UIColor(rgb: UInt32(bitPattern: innerColor)).cgColor, UIColor(rgb: UInt32(bitPattern: outerColor)).cgColor]) + }, additionalLeftIcon: { theme in + return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in getController()?.dismiss(result: .dismissWithoutContent, completion: nil) @@ -212,6 +213,8 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu private var totalContentHeight: CGFloat = 0 private var itemFrames: [AnyHashable: CGRect] = [:] + let needsPadding: Bool = false + init(presentationData: PresentationData, item: GiftAttributeListContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { self.item = item self.presentationData = presentationData.withUpdate(listsFontSize: .regular) @@ -286,19 +289,12 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu yOffset += UIScreenPixel } - for (index, attribute) in effectiveAttributes.enumerated() { + for (_, attribute) in effectiveAttributes.enumerated() { let attributeId = self.getAttributeId(from: attribute) let height = self.itemHeights[attributeId] ?? defaultHeight let frame = CGRect(x: 0, y: yOffset, width: constrainedWidth, height: height) items.append((attributeId, .attribute(attribute), frame)) yOffset += height - - if index < effectiveAttributes.count - 1 { - let separatorId = AnyHashable("separator_\(attributeId)") - let separatorFrame = CGRect(x: 0, y: yOffset, width: constrainedWidth, height: UIScreenPixel) - items.append((separatorId, .separator, separatorFrame)) - yOffset += UIScreenPixel - } } if !self.searchQuery.isEmpty && effectiveAttributes.isEmpty { @@ -467,7 +463,7 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let minActionsWidth: CGFloat = 250.0 + let minActionsWidth: CGFloat = 270.0 let maxActionsWidth: CGFloat = 300.0 let constrainedWidth = min(constrainedWidth, maxActionsWidth) let maxWidth = max(constrainedWidth, minActionsWidth) @@ -503,7 +499,7 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu } func canBeHighlighted() -> Bool { - return self.isActionEnabled + return false } func updateIsHighlighted(isHighlighted: Bool) { diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index 8417e332bb..493c39d9bc 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -133,9 +133,9 @@ final class GiftStoreScreenComponent: Component { self.scrollView.delegate = self self.addSubview(self.scrollView) - self.addSubview(self.loadingView) self.addSubview(self.edgeEffectView) + self.addSubview(self.loadingView) self.scrollView.layer.addSublayer(self.topOverscrollLayer) } @@ -415,7 +415,7 @@ final class GiftStoreScreenComponent: Component { if view.superview == nil { view.alpha = 0.0 fadeTransition.setAlpha(view: view, alpha: 1.0) - self.insertSubview(view, belowSubview: self.loadingView) + self.insertSubview(view, aboveSubview: self.scrollView) view.playOnce() } view.bounds = CGRect(origin: .zero, size: emptyResultsAnimationFrame.size) @@ -425,7 +425,7 @@ final class GiftStoreScreenComponent: Component { if view.superview == nil { view.alpha = 0.0 fadeTransition.setAlpha(view: view, alpha: 1.0) - self.insertSubview(view, belowSubview: self.loadingView) + self.insertSubview(view, aboveSubview: self.scrollView) } view.bounds = CGRect(origin: .zero, size: emptyResultsTitleFrame.size) ComponentTransition.immediate.setPosition(view: view, position: emptyResultsTitleFrame.center) @@ -1203,7 +1203,7 @@ final class GiftStoreScreenComponent: Component { } else { loadingTransition.setAlpha(view: self.loadingView, alpha: 0.0) } - transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight), size: availableSize)) + transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight + 10.0), size: availableSize)) return availableSize } diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/SearchContextItem.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/SearchContextItem.swift index 585a863f69..2ce78ae467 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/SearchContextItem.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/SearchContextItem.swift @@ -80,7 +80,7 @@ private final class SearchContextItemNode: ASDisplayNode, ContextMenuCustomNode, environment: {}, containerSize: size ) - let iconFrame = CGRect(origin: CGPoint(x: 17.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) + let iconFrame = CGRect(origin: CGPoint(x: 23.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) if let iconView = self.icon.view { if iconView.superview == nil { self.view.addSubview(iconView) @@ -88,7 +88,7 @@ private final class SearchContextItemNode: ASDisplayNode, ContextMenuCustomNode, transition.setFrame(view: iconView, frame: iconFrame) } - let inputInset: CGFloat = 42.0 + let inputInset: CGFloat = 46.0 self.inputField.parentState = self.state let inputFieldSize = self.inputField.update( diff --git a/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift b/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift index 789b261304..e9a4727b25 100644 --- a/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift +++ b/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift @@ -305,6 +305,8 @@ public final class HorizontalTabsComponent: Component { private var tabSwitchFraction: CGFloat = 0.0 private var isDraggingTabs: Bool = false private var temporaryLiftTimer: Foundation.Timer? + private var didTapOnAnItem: Bool = false + private var didTapOnAnItemTimer: Foundation.Timer? private var tapRecognizer: UITapGestureRecognizer? @@ -531,6 +533,14 @@ public final class HorizontalTabsComponent: Component { for (id, itemView) in self.itemViews { if self.scrollView.convert(itemView.selectionFrame, to: self).contains(point) { if let tab = component.tabs.first(where: { $0.id == id }) { + self.didTapOnAnItem = true + self.didTapOnAnItemTimer?.invalidate() + self.didTapOnAnItemTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { [weak self] _ in + guard let self else { + return + } + self.didTapOnAnItem = false + }) tab.action() } } @@ -609,7 +619,7 @@ public final class HorizontalTabsComponent: Component { self.temporaryLiftTimer?.invalidate() self.temporaryLiftTimer = nil - if !transition.animation.isImmediate { + if !transition.animation.isImmediate && self.didTapOnAnItem { self.temporaryLiftTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { [weak self] _ in guard let self else { return @@ -627,6 +637,12 @@ public final class HorizontalTabsComponent: Component { self.component = component self.state = state + self.didTapOnAnItem = false + if let didTapOnAnItemTimer = self.didTapOnAnItemTimer { + self.didTapOnAnItemTimer = nil + didTapOnAnItemTimer.invalidate() + } + self.reorderingGesture?.isEnabled = component.isEditing let sizeHeight: CGFloat = availableSize.height diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift index 1c743b30e9..317171905f 100644 --- a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift @@ -222,7 +222,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { if let backgroundContainer = self.backgroundContainer { backgroundContainer.contentView.addSubview(titleView) } else { - self.buttonsContainerNode.view.addSubview(titleView) + self.buttonsContainerNode.view.insertSubview(titleView, at: 0) } } @@ -814,10 +814,13 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { edgeEffectView.isHidden = true } else { edgeEffectView.isHidden = false - let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight + 20.0 + 20.0)) + + let edgeEffectHeight: CGFloat = size.height + additionalBackgroundHeight + 24.0 + + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: 20.0 + edgeEffectHeight)) transition.updatePosition(layer: edgeEffectView.layer, position: edgeEffectFrame.center) transition.updateBounds(layer: edgeEffectView.layer, bounds: CGRect(origin: CGPoint(), size: edgeEffectFrame.size)) - edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: 50.0, transition: ComponentTransition(transition)) + edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: min(64.0, edgeEffectHeight), transition: ComponentTransition(transition)) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 70819bdb50..eb55763bbe 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -228,6 +228,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let edgeEffectView: EdgeEffectView let headerNode: PeerInfoHeaderNode + var underHeaderContentsAlpha: CGFloat = 1.0 var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] let paneContainerNode: PeerInfoPaneContainerNode @@ -2059,6 +2060,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } + self.underHeaderContentsAlpha = alpha if !self.state.isEditing { for (_, section) in self.regularSections { transition.updateAlpha(node: section, alpha: alpha) @@ -5293,7 +5295,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing { sectionNode.alpha = 0.0 - transition.updateAlpha(node: sectionNode, alpha: 1.0, delay: 0.1) + transition.updateAlpha(node: sectionNode, alpha: self.underHeaderContentsAlpha, delay: 0.1) } let sectionWidth = layout.size.width - insets.left - insets.right @@ -5312,7 +5314,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing { } else { - transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : 1.0) + transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : self.underHeaderContentsAlpha) } if !sectionHeight.isZero && !self.state.isEditing { contentHeight += sectionHeight diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift index 97da4e36fb..c4a0ff8b21 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift @@ -360,7 +360,7 @@ extension PeerInfoScreenNode { items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in if !showPhotos { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak pane] _, a in @@ -384,7 +384,7 @@ extension PeerInfoScreenNode { }))) items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in if !showVideos { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak pane] _, a in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift index fa16166d34..b0628e90b5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift @@ -113,7 +113,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom private let getController: () -> ContextControllerProtocol? private let actionSelected: (ContextMenuActionResult) -> Void - private let highlightedBackgroundNode: ASDisplayNode private let buttonNode: HighlightTrackingButtonNode private let textNode: ImmediateTextNode private let avatarNode: AvatarNode @@ -127,11 +126,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0) - self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isAccessibilityElement = false - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - self.highlightedBackgroundNode.alpha = 0.0 - self.textNode = ImmediateTextNode() self.textNode.isAccessibilityElement = false self.textNode.isUserInteractionEnabled = false @@ -150,29 +144,17 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom super.init() - self.addSubnode(self.highlightedBackgroundNode) self.addSubnode(self.textNode) self.addSubnode(self.avatarNode) self.addSubnode(self.buttonNode) - self.buttonNode.highligthedChanged = { [weak self] highligted in - guard let strongSelf = self else { - return - } - if highligted { - strongSelf.highlightedBackgroundNode.alpha = 1.0 - } else { - strongSelf.highlightedBackgroundNode.alpha = 0.0 - strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - } self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 12.0 - let verticalInset: CGFloat = 12.0 + let sideInset: CGFloat = 18.0 + let iconSideInset: CGFloat = 20.0 + let verticalInset: CGFloat = 11.0 let iconSize = CGSize(width: 28.0, height: 28.0) @@ -192,7 +174,7 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in let verticalOrigin = floor((size.height - textSize.height) / 2.0) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) var iconContent: EmojiStatusComponent.Content? @@ -229,16 +211,13 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom } } - transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) + transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) - transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) }) } func updateTheme(presentationData: PresentationData) { - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - if let attributedText = self.textNode.attributedText { let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText) updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length)) @@ -255,11 +234,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom } func setIsHighlighted(_ value: Bool) { - if value { - self.highlightedBackgroundNode.alpha = 1.0 - } else { - self.highlightedBackgroundNode.alpha = 0.0 - } } func updateIsHighlighted(isHighlighted: Bool) { diff --git a/submodules/TelegramUI/Components/SectionTitleContextItem/Sources/SectionTitleContextItem.swift b/submodules/TelegramUI/Components/SectionTitleContextItem/Sources/SectionTitleContextItem.swift index 03cb0e9a23..c24064e91a 100644 --- a/submodules/TelegramUI/Components/SectionTitleContextItem/Sources/SectionTitleContextItem.swift +++ b/submodules/TelegramUI/Components/SectionTitleContextItem/Sources/SectionTitleContextItem.swift @@ -30,6 +30,10 @@ private final class SectionTitleContextItemNode: ASDisplayNode, ContextMenuCusto return false } + var needsPadding: Bool { + return false + } + init(presentationData: PresentationData, item: SectionTitleContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { self.item = item self.presentationData = presentationData @@ -56,17 +60,17 @@ private final class SectionTitleContextItemNode: ASDisplayNode, ContextMenuCusto } func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 + let sideInset: CGFloat = 18.0 + 4.0 let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - sideInset, height: .greatestFiniteMagnitude)) - let height: CGFloat = 28.0 + let height: CGFloat = 10.0 + 28.0 return (CGSize(width: textSize.width + sideInset + sideInset, height: height), { size, transition in - let verticalOrigin = floor((size.height - textSize.height) / 2.0) + let verticalOrigin = floor((size.height - 10.0 - textSize.height) / 2.0) let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - 10.0))) }) } diff --git a/submodules/TelegramUI/Components/SliderContextItem/Sources/SliderContextItem.swift b/submodules/TelegramUI/Components/SliderContextItem/Sources/SliderContextItem.swift index bd7a4fef68..33a32b1562 100644 --- a/submodules/TelegramUI/Components/SliderContextItem/Sources/SliderContextItem.swift +++ b/submodules/TelegramUI/Components/SliderContextItem/Sources/SliderContextItem.swift @@ -47,12 +47,17 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode, let title: String? let minValue: CGFloat let maxValue: CGFloat + var suppressAnimation = false var value: CGFloat = 1.0 { didSet { - self.updateValue(transition: .animated(duration: 0.2, curve: .spring)) + self.updateValue(transition: self.suppressAnimation ? .immediate :.animated(duration: 0.2, curve: .spring)) } } + public var needsPadding: Bool { + return true + } + private let valueChanged: (CGFloat, Bool) -> Void private let hapticFeedback = HapticFeedback() @@ -167,11 +172,21 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode, } private func updateValue(transition: ContainedViewLayoutTransition = .immediate) { - let width = self.frame.width + let sideInset: CGFloat = 10.0 + let width = self.frame.width - sideInset * 2.0 let range = self.maxValue - self.minValue let value = (self.value - self.minValue) / range - transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value * width, height: self.frame.height))) + var foregroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: value * width, height: self.frame.height)) + var foregroundOffset: CGFloat = 0.0 + if foregroundFrame.width < 40.0 { + foregroundOffset = (40.0 - foregroundFrame.width) * 0.5 + foregroundFrame = foregroundFrame.insetBy(dx: 0.0, dy: foregroundOffset) + } + transition.updateFrameAdditive(node: self.foregroundNode, frame: foregroundFrame) + transition.updateSublayerTransformOffset(layer: self.foregroundNode.layer, offset: CGPoint(x: 0.0, y: -foregroundOffset)) + + transition.updateCornerRadius(node: self.foregroundNode, cornerRadius: min(20.0, foregroundFrame.width * 0.5)) let stringValue = String(format: "%.1fx", self.value) @@ -221,9 +236,10 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode, let _ = self.foregroundTitleNode.updateLayout(CGSize(width: 120.0, height: 100.0)) return (CGSize(width: height * 3.0, height: height), { size, transition in - let leftInset: CGFloat = 17.0 + let leftInset: CGFloat = 10.0 + let sideInset: CGFloat = 10.0 - self.vibrancyEffectView?.frame = CGRect(origin: .zero, size: size) + self.vibrancyEffectView?.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: size.width - sideInset * 2.0, height: size.height)) let backgroundTextWidth = self.backgroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true).width @@ -235,18 +251,18 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode, titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - backgroundTitleSize.height) / 2.0)), size: backgroundTitleSize) if self.title != nil { - textFrame = CGRect(origin: CGPoint(x: size.width - leftInset - backgroundTextWidth, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize) + textFrame = CGRect(origin: CGPoint(x: size.width - sideInset - leftInset - backgroundTextWidth, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize) } else { - textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize) + textFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize) } transition.updateFrameAdditive(node: self.dimBackgroundTitleNode, frame: titleFrame) - transition.updateFrameAdditive(node: self.backgroundTitleNode, frame: titleFrame) + transition.updateFrameAdditive(node: self.backgroundTitleNode, frame: titleFrame.offsetBy(dx: sideInset, dy: 0.0)) transition.updateFrameAdditive(node: self.foregroundTitleNode, frame: titleFrame) transition.updateFrameAdditive(node: self.dimBackgroundTextNode, frame: textFrame) transition.updateFrameAdditive(node: self.backgroundTextNode, frame: textFrame) - transition.updateFrameAdditive(node: self.foregroundTextNode, frame: textFrame) + transition.updateFrameAdditive(node: self.foregroundTextNode, frame: textFrame.offsetBy(dx: -sideInset, dy: 0.0)) }) } @@ -260,7 +276,9 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode, let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x let delta = translation / self.bounds.width * range + self.suppressAnimation = true self.value = max(self.minValue, min(self.maxValue, self.value + delta)) + self.suppressAnimation = false gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view) if self.value == 2.0 && previousValue != 2.0 { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 3e831855b0..b32aae553a 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2242,14 +2242,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { edgeEffectAlpha = self.chatPresentationInterfaceState.chatWallpaper.singleColor != nil ? 0.85 : 0.75 } + var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? - if let current = self.bottomBackgroundEdgeEffectNode { - bottomBackgroundEdgeEffectNode = current - } else { - if let value = self.backgroundNode.makeEdgeEffectNode() { - bottomBackgroundEdgeEffectNode = value - self.bottomBackgroundEdgeEffectNode = value - self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + if self.historyNode.rotated { + if let current = self.bottomBackgroundEdgeEffectNode { + bottomBackgroundEdgeEffectNode = current + } else { + if let value = self.backgroundNode.makeEdgeEffectNode() { + bottomBackgroundEdgeEffectNode = value + self.bottomBackgroundEdgeEffectNode = value + self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + } } } if let bottomBackgroundEdgeEffectNode { @@ -2451,13 +2454,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { }) var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? - if let current = self.topBackgroundEdgeEffectNode { - topBackgroundEdgeEffectNode = current - } else { - if let value = self.backgroundNode.makeEdgeEffectNode() { - topBackgroundEdgeEffectNode = value - self.topBackgroundEdgeEffectNode = value - self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + if self.historyNode.rotated { + if let current = self.topBackgroundEdgeEffectNode { + topBackgroundEdgeEffectNode = current + } else { + if let value = self.backgroundNode.makeEdgeEffectNode() { + topBackgroundEdgeEffectNode = value + self.topBackgroundEdgeEffectNode = value + self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + } } } if let topBackgroundEdgeEffectNode { diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift index 616a8f9493..44c8a76ba4 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift @@ -70,6 +70,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { } self.badgeBackgroundView = GlassBackgroundView() + self.badgeBackgroundView.isUserInteractionEnabled = false self.badgeBackgroundView.alpha = 0.0 self.badgeTextNode = ImmediateAnimatedCountLabelNode() diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index f72eb2c359..ddd2f2a67f 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -178,10 +178,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController self.title = self.titleProducer(self.presentationData.strings) if glass { - } else { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) } if self.multipleSelection == .always { @@ -413,15 +411,6 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments { - switch presentationArguments.presentationAnimation { - case .modalSheet: - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed)) - case .none: - break - } - } - self.contactsNode.contactListNode.enableUpdates = true }