diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 8802bb07f9..6e376e79fa 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -10,8 +10,6 @@ "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", - "https://bcr.bazel.build/modules/aexml/4.7.0/MODULE.bazel": "4030ff1555ade0956c08c74722851fcca0dc02ec7b8e7c61d0bbc4806ec4b2de", - "https://bcr.bazel.build/modules/aexml/4.7.0/source.json": "641c9de95dc10b8bf3685b5de9f9a84e7470ec3e40a09d7ddc9e3c9f2289a931", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", @@ -20,6 +18,7 @@ "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", @@ -50,8 +49,6 @@ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", - "https://bcr.bazel.build/modules/pathkit/1.0.1/MODULE.bazel": "fae93989a10f8d90d5ac02453e6632ae7f71111687862c01f468858cef40bb5e", - "https://bcr.bazel.build/modules/pathkit/1.0.1/source.json": "3215e6b4b08f96f34024eaf186d247744ca255925d7ee3f50cf94f7cf885696b", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", @@ -86,7 +83,8 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", - "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", + "https://bcr.bazel.build/modules/rules_cc/0.1.2/source.json": "53fcb09b5816c83ca60d9d7493faf3bfaf410dfc2f15deb52d6ddd146b8d43f0", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", @@ -137,7 +135,8 @@ "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13", "https://bcr.bazel.build/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", - "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", @@ -148,8 +147,6 @@ "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/xcodeproj/8.27.3/MODULE.bazel": "49276599207dae3df1e4336c2067505323dfb0606b53ef63e144087d1226e0eb", - "https://bcr.bazel.build/modules/xcodeproj/8.27.3/source.json": "bbbb718187dcbdfbb3a9a0ec7d49446cdf48c67657cafd79b5cf33aa8918f608", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", @@ -157,37 +154,6 @@ }, "selectedYankedVersions": {}, "moduleExtensions": { - "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": { - "general": { - "bzlTransitiveDigest": "gv4nokEMGNye4Jvoh7Tw0Lzs63zfklj+n4t0UegI7Ms=", - "usagesDigest": "lfcV4HxPD+NLaRIT/v7BtSGFgE7c9xrWU7jDiwBAxzo=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "local_config_apple_cc_toolchains": { - "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains", - "attributes": {} - }, - "local_config_apple_cc": { - "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "apple_support+", - "bazel_tools", - "bazel_tools" - ], - [ - "bazel_tools", - "rules_cc", - "rules_cc+" - ] - ] - } - }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "hUTp2w+RUVdL7ma5esCXZJAFnX7vLbVfLd7FwnQI6bU=", @@ -290,7 +256,7 @@ }, "@@rules_xcodeproj+//xcodeproj:extensions.bzl%internal": { "general": { - "bzlTransitiveDigest": "6MYik+6MZUO7rOzaI0dUJYVD8dJrR1Q2rT+5vo1j73U=", + "bzlTransitiveDigest": "/r7IWMTdpceHqqiSh7G9bQLHvIkfqE/cesswbkTSJZw=", "usagesDigest": "fvsnMonVwKDYnBfww4bXuYie3WU0d9VSqT2gePSdQco=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -312,7 +278,7 @@ }, "@@rules_xcodeproj+//xcodeproj:extensions.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "6MYik+6MZUO7rOzaI0dUJYVD8dJrR1Q2rT+5vo1j73U=", + "bzlTransitiveDigest": "/r7IWMTdpceHqqiSh7G9bQLHvIkfqE/cesswbkTSJZw=", "usagesDigest": "jzxYhnOC9BE0dJ0biFLfxWXi/+R19uAAZkJ0p9CY0JI=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -345,15 +311,6 @@ "url": "https://github.com/apple/swift-argument-parser/archive/refs/tags/1.2.3.tar.gz" } }, - "com_github_tadija_aexml": { - "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", - "attributes": { - "build_file_content": "load(\"@build_bazel_rules_swift//swift:swift.bzl\", \"swift_library\")\n\nswift_library(\n name = \"AEXML\",\n srcs = glob([\"Sources/AEXML/**/*.swift\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "sha256": "5a76c28e4fa9dcc1cbfb87a8518652628e990e522ecfbc98bdad17eabf4631d5", - "strip_prefix": "AEXML-4.6.1", - "url": "https://github.com/tadija/AEXML/archive/refs/tags/4.6.1.tar.gz" - } - }, "com_github_michaeleisel_jjliso8601dateformatter": { "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 289cfa96d8..aab2bb49ec 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -847,7 +847,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return } strongSelf.forwardMessages(messageIds: nil) - }, displayCopyProtectionTip: { [weak self] node, save in + }, displayCopyProtectionTip: { [weak self] view, save in guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else { return } @@ -910,7 +910,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } strongSelf.present?(tooltipController, TooltipControllerPresentationArguments(sourceNodeAndRect: { if let strongSelf = self { - let rect = node.view.convert(node.view.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) + let rect = view.convert(view.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) return (strongSelf, rect) } return nil diff --git a/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift index 854d2153ef..cfe3225c41 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift @@ -17,7 +17,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { private let deleteMessages: () -> Void private let shareMessages: () -> Void private let forwardMessages: () -> Void - private let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void + private let displayCopyProtectionTip: (UIView, Bool) -> Void private let separatorNode: ASDisplayNode private let backgroundNode: NavigationBackgroundNode @@ -61,7 +61,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { } } - init(context: AccountContext, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void) { + init(context: AccountContext, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (UIView, Bool) -> Void) { self.context = context self.deleteMessages = deleteMessages self.shareMessages = shareMessages @@ -176,7 +176,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { @objc func forwardButtonPressed() { if let actions = self.actions, actions.isCopyProtected { - self.displayCopyProtectionTip(self.forwardButton, false) + self.displayCopyProtectionTip(self.forwardButton.view, false) } else { self.forwardMessages() } @@ -184,7 +184,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { @objc func shareButtonPressed() { if let actions = self.actions, actions.isCopyProtected { - self.displayCopyProtectionTip(self.shareButton, true) + self.displayCopyProtectionTip(self.shareButton.view, true) } else { self.shareMessages() } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift index 57f9b9ef08..54e4686df8 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift @@ -166,7 +166,7 @@ public final class ChatPanelInterfaceInteraction { public let openInviteRequests: () -> Void public let openSendAsPeer: (ASDisplayNode, ContextGesture?) -> Void public let presentChatRequestAdminInfo: () -> Void - public let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void + public let displayCopyProtectionTip: (UIView, Bool) -> Void public let openWebView: (String, String, Bool, ChatOpenWebViewSource) -> Void public let updateShowWebView: ((Bool) -> Bool) -> Void public let insertText: (NSAttributedString) -> Void @@ -294,7 +294,7 @@ public final class ChatPanelInterfaceInteraction { openInviteRequests: @escaping () -> Void, openSendAsPeer: @escaping (ASDisplayNode, ContextGesture?) -> Void, presentChatRequestAdminInfo: @escaping () -> Void, - displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void, + displayCopyProtectionTip: @escaping (UIView, Bool) -> Void, openWebView: @escaping (String, String, Bool, ChatOpenWebViewSource) -> Void, updateShowWebView: @escaping ((Bool) -> Bool) -> Void, insertText: @escaping (NSAttributedString) -> Void, diff --git a/submodules/ChatSendMessageActionUI/BUILD b/submodules/ChatSendMessageActionUI/BUILD index f020dd238a..58b4cdab9e 100644 --- a/submodules/ChatSendMessageActionUI/BUILD +++ b/submodules/ChatSendMessageActionUI/BUILD @@ -38,6 +38,7 @@ swift_library( "//submodules/ActivityIndicator", "//submodules/UndoUI", "//submodules/RadialStatusNode", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift index b5f4e7606f..8d38956739 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift @@ -1116,15 +1116,15 @@ final class ChatSendMessageContextScreenComponent: Component { } } - let sendButtonSize = CGSize(width: min(sourceSendButtonFrame.width, 44.0), height: sourceSendButtonFrame.height) + let sendButtonSize = CGSize(width: min(sourceSendButtonFrame.width, 40.0), height: sourceSendButtonFrame.height) var readySendButtonFrame = CGRect(origin: CGPoint(x: sourceSendButtonFrame.maxX - sendButtonSize.width, y: sourceSendButtonFrame.minY), size: sendButtonSize) - var sourceActionsStackFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 1.0 - actionsStackSize.width, y: sourceMessageItemFrame.maxY + messageActionsSpacing), size: actionsStackSize) + var sourceActionsStackFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 1.0 - 8.0 - actionsStackSize.width, y: sourceMessageItemFrame.maxY + messageActionsSpacing), size: actionsStackSize) if !isMessageVisible { sourceActionsStackFrame.origin.y = sourceSendButtonFrame.maxY - sourceActionsStackFrame.height - 5.0 } - var readyMessageItemFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 8.0 - messageItemSize.width, y: readySendButtonFrame.maxY - 6.0 - messageItemSize.height), size: messageItemSize) + var readyMessageItemFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 0.0 - messageItemSize.width, y: readySendButtonFrame.maxY - 6.0 - messageItemSize.height), size: messageItemSize) if let mediaPreview { switch mediaPreview.layoutType { case .message, .media: diff --git a/submodules/ChatSendMessageActionUI/Sources/SendButton.swift b/submodules/ChatSendMessageActionUI/Sources/SendButton.swift index 74ca91380d..d703a2f618 100644 --- a/submodules/ChatSendMessageActionUI/Sources/SendButton.swift +++ b/submodules/ChatSendMessageActionUI/Sources/SendButton.swift @@ -17,6 +17,7 @@ import WallpaperBackgroundNode import AppBundle import ActivityIndicator import RadialStatusNode +import GlassBackgroundComponent final class SendButton: HighlightTrackingButton { enum Kind { @@ -27,8 +28,7 @@ final class SendButton: HighlightTrackingButton { private let kind: Kind private let containerView: UIView - private var backgroundContent: WallpaperBubbleBackgroundNode? - private let backgroundLayer: SimpleLayer + private let backgroundView: GlassBackgroundView private let iconView: UIImageView private var activityIndicator: RadialStatusNode? @@ -41,7 +41,7 @@ final class SendButton: HighlightTrackingButton { self.containerView = UIView() self.containerView.isUserInteractionEnabled = false - self.backgroundLayer = SimpleLayer() + self.backgroundView = GlassBackgroundView() self.iconView = UIImageView() self.iconView.isUserInteractionEnabled = false @@ -51,7 +51,7 @@ final class SendButton: HighlightTrackingButton { self.containerView.clipsToBounds = true self.addSubview(self.containerView) - self.containerView.layer.addSublayer(self.backgroundLayer) + self.containerView.addSubview(self.backgroundView) self.containerView.addSubview(self.iconView) } @@ -69,34 +69,13 @@ final class SendButton: HighlightTrackingButton { size: CGSize, transition: ComponentTransition ) { - let innerSize = CGSize(width: size.width - 5.5 * 2.0, height: 33.0) + let innerSize = CGSize(width: size.width, height: size.height) let containerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - innerSize.width) * 0.5), y: floorToScreenPixels((size.height - innerSize.height) * 0.5)), size: innerSize) transition.setFrame(view: self.containerView, frame: containerFrame) transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: innerSize.height * 0.5) - if self.window != nil { - if self.backgroundContent == nil, let backgroundNode = backgroundNode as? WallpaperBackgroundNodeImpl { - if let backgroundContent = backgroundNode.makeLegacyBubbleBackground(for: .outgoing) { - self.backgroundContent = backgroundContent - self.containerView.insertSubview(backgroundContent.view, at: 0) - } - } - } - - if let backgroundContent = self.backgroundContent { - transition.setFrame(view: backgroundContent.view, frame: CGRect(origin: CGPoint(), size: innerSize)) - } - - if backgroundNode != nil && [.day, .night].contains(presentationData.theme.referenceTheme.baseTheme) && !presentationData.theme.chat.message.outgoing.bubble.withWallpaper.hasSingleFillColor { - self.backgroundContent?.isHidden = false - self.backgroundLayer.isHidden = true - } else { - self.backgroundContent?.isHidden = true - self.backgroundLayer.isHidden = false - } - - self.backgroundLayer.backgroundColor = presentationData.theme.chat.inputPanel.actionControlFillColor.cgColor - transition.setFrame(layer: self.backgroundLayer, frame: CGRect(origin: CGPoint(), size: innerSize)) + self.backgroundView.update(size: innerSize, cornerRadius: innerSize.height * 0.5, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: presentationData.theme.chat.inputPanel.actionControlFillColor), transition: .immediate) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: innerSize)) if self.previousIsAnimatedIn != isAnimatedIn { self.previousIsAnimatedIn = isAnimatedIn @@ -140,7 +119,7 @@ final class SendButton: HighlightTrackingButton { } if let icon = self.iconView.image { - let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((innerSize.width - icon.size.width) * 0.5) - UIScreenPixel, y: floorToScreenPixels((innerSize.height - icon.size.height) * 0.5)), size: icon.size) + let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((innerSize.width - icon.size.width) * 0.5), y: floorToScreenPixels((innerSize.height - icon.size.height) * 0.5)), size: icon.size) transition.setPosition(view: self.iconView, position: iconFrame.center) transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) @@ -198,8 +177,5 @@ final class SendButton: HighlightTrackingButton { } func updateGlobalRect(rect: CGRect, within containerSize: CGSize, transition: ComponentTransition) { - if let backgroundContent = self.backgroundContent { - backgroundContent.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.containerView.frame.minX, y: rect.minY + self.containerView.frame.minY), size: backgroundContent.bounds.size), within: containerSize, transition: transition.containedViewLayoutTransition) - } } } diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index 79eec853d5..10f5d42a24 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -151,7 +151,13 @@ final class TabBarControllerNode: ASDisplayNode { transition.updateAlpha(node: self.disabledOverlayNode, alpha: value ? 0.0 : 1.0) } - var tabBarHidden = false + var tabBarHidden = false { + didSet { + if self.tabBarHidden != oldValue { + self.requestUpdate() + } + } + } func containerLayoutUpdated(_ layout: ContainerViewLayout, toolbar: Toolbar?, transition: ContainedViewLayoutTransition) -> CGFloat { let params = Params(layout: layout, toolbar: toolbar) diff --git a/submodules/TelegramUI/Components/Chat/ChatInputMessageAccessoryPanel/Sources/ChatInputMessageAccessoryPanel.swift b/submodules/TelegramUI/Components/Chat/ChatInputMessageAccessoryPanel/Sources/ChatInputMessageAccessoryPanel.swift index 328ce00df3..06fca75090 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputMessageAccessoryPanel/Sources/ChatInputMessageAccessoryPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputMessageAccessoryPanel/Sources/ChatInputMessageAccessoryPanel.swift @@ -331,7 +331,7 @@ public final class ChatInputMessageAccessoryPanel: Component { let environment = environment[EnvironmentType.self].value - if self.component == nil { + let messageIdsFromComponent: (ChatInputMessageAccessoryPanel) -> [EngineMessage.Id] = { component in let messageIds: [EngineMessage.Id] switch component.contents { case let .edit(edit): @@ -343,7 +343,11 @@ public final class ChatInputMessageAccessoryPanel: Component { case .linkPreview, .suggestPost: messageIds = [] } - + return messageIds + } + + let messageIds = messageIdsFromComponent(component) + if self.component == nil || self.component.flatMap(messageIdsFromComponent) != messageIds { self.contentDisposable?.dispose() if !messageIds.isEmpty { self.contentDisposable = (component.context.engine.data.subscribe( diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD index 4955cb74eb..159767d0a3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/BUILD @@ -22,6 +22,9 @@ swift_library( "//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/TelegramUI/Components/Chat/TopMessageReactions", "//submodules/ReactionSelectionNode", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift index abcac90dc6..14cd78cf46 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift @@ -13,6 +13,9 @@ import ChatInputPanelNode import ReactionSelectionNode import EntityKeyboard import TopMessageReactions +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters private final class ChatMessageSelectionInputPanelNodeViewForOverlayContent: UIView, ChatInputPanelViewForOverlayContent { var reactionContextNode: ReactionContextNode? @@ -61,14 +64,125 @@ private final class ChatMessageSelectionInputPanelNodeViewForOverlayContent: UIV } } +private final class GlassButtonView: HighlightTrackingButton { + private struct Params: Equatable { + let theme: PresentationTheme + let size: CGSize + + init(theme: PresentationTheme, size: CGSize) { + self.theme = theme + self.size = size + } + + static func ==(lhs: Params, rhs: Params) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.size != rhs.size { + return false + } + return true + } + } + + private let backgroundView: GlassBackgroundView + private let iconView: GlassBackgroundView.ContentImageView + + private var params: Params? + + var isImplicitlyDisabled: Bool = false { + didSet { + self.updateIsEnabled() + } + } + + override var isEnabled: Bool { + didSet { + self.updateIsEnabled() + } + } + + var icon: String? { + didSet { + if self.icon == oldValue { + return + } + if let icon = self.icon { + self.iconView.image = UIImage(bundleImageName: icon)?.withRenderingMode(.alwaysTemplate) + } else { + self.iconView.image = nil + } + if let params = self.params { + self.updateImpl(params: params, transition: .immediate) + } + } + } + + override init(frame: CGRect) { + self.backgroundView = GlassBackgroundView() + self.backgroundView.isUserInteractionEnabled = false + + self.iconView = GlassBackgroundView.ContentImageView() + self.backgroundView.contentView.addSubview(self.iconView) + + super.init(frame: frame) + + self.addSubview(self.backgroundView) + + self.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted && self.isEnabled && !self.isImplicitlyDisabled { + self.backgroundView.contentView.alpha = 0.6 + } else { + self.backgroundView.contentView.alpha = 1.0 + self.backgroundView.contentView.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(theme: PresentationTheme, size: CGSize, transition: ComponentTransition) { + let params = Params(theme: theme, size: size) + if self.params != params { + self.iconView.tintColor = params.theme.chat.inputPanel.inputControlColor + self.params = params + self.updateImpl(params: params, transition: transition) + } + } + + private func updateImpl(params: Params, transition: ComponentTransition) { + if let image = self.iconView.image { + let iconFrame = image.size.centered(in: CGRect(origin: CGPoint(), size: params.size)) + transition.setFrame(view: self.iconView, frame: iconFrame) + } + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) + self.backgroundView.update(size: params.size, cornerRadius: min(params.size.width, params.size.height) * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition) + + let isEnabled = self.isEnabled && !self.isImplicitlyDisabled + self.iconView.alpha = isEnabled ? 1.0 : 0.5 + self.iconView.tintMask.alpha = self.iconView.alpha + } + + private func updateIsEnabled() { + if let params = self.params { + self.updateImpl(params: params, transition: .immediate) + } + } +} + public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { - private let deleteButton: HighlightableButtonNode - private let reportButton: HighlightableButtonNode - private let forwardButton: HighlightableButtonNode - private let shareButton: HighlightableButtonNode - private let tagButton: HighlightableButtonNode - private let tagEditButton: HighlightableButtonNode - private let separatorNode: ASDisplayNode + private let deleteButton: GlassButtonView + private let reportButton: GlassButtonView + private let forwardButton: GlassButtonView + private let shareButton: GlassButtonView + private let tagButton: GlassButtonView + private let tagEditButton: GlassButtonView private let reactionOverlayContainer: ChatMessageSelectionInputPanelNodeViewForOverlayContent @@ -93,69 +207,60 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { self.theme = theme self.peerMedia = peerMedia - self.deleteButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.deleteButton = GlassButtonView() + self.deleteButton.icon = "Chat/Input/Accessory Panels/MessageSelectionTrash" self.deleteButton.isEnabled = false self.deleteButton.isAccessibilityElement = true self.deleteButton.accessibilityLabel = strings.VoiceOver_MessageContextDelete - self.reportButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.reportButton = GlassButtonView() + self.reportButton.icon = "Chat/Input/Accessory Panels/MessageSelectionReport" self.reportButton.isEnabled = false self.reportButton.isAccessibilityElement = true self.reportButton.accessibilityLabel = strings.VoiceOver_MessageContextReport - self.forwardButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.forwardButton = GlassButtonView() + self.forwardButton.icon = "Chat/Input/Accessory Panels/MessageSelectionForward" self.forwardButton.isAccessibilityElement = true self.forwardButton.accessibilityLabel = strings.VoiceOver_MessageContextForward - self.shareButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.shareButton = GlassButtonView() + self.shareButton.icon = "Chat/Input/Accessory Panels/MessageSelectionAction" self.shareButton.isAccessibilityElement = true self.shareButton.accessibilityLabel = strings.VoiceOver_MessageContextShare - self.tagButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.tagButton = GlassButtonView() + self.tagButton.icon = "Chat/Input/Accessory Panels/TagIcon" self.tagButton.isAccessibilityElement = true self.tagButton.accessibilityLabel = strings.VoiceOver_MessageSelectionButtonTag - self.tagEditButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0))) + self.tagEditButton = GlassButtonView() + self.tagEditButton.icon = "Chat/Input/Accessory Panels/TagEditIcon" self.tagEditButton.isAccessibilityElement = true self.tagEditButton.accessibilityLabel = strings.VoiceOver_MessageSelectionButtonTag - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.tagButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TagIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.tagEditButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TagEditIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - - self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor - self.reactionOverlayContainer = ChatMessageSelectionInputPanelNodeViewForOverlayContent() super.init() - self.addSubnode(self.deleteButton) - self.addSubnode(self.reportButton) - self.addSubnode(self.forwardButton) - self.addSubnode(self.shareButton) - self.addSubnode(self.tagButton) - self.addSubnode(self.tagEditButton) - self.addSubnode(self.separatorNode) + self.view.addSubview(self.deleteButton) + self.view.addSubview(self.reportButton) + self.view.addSubview(self.forwardButton) + self.view.addSubview(self.shareButton) + self.view.addSubview(self.tagButton) + self.view.addSubview(self.tagEditButton) self.viewForOverlayContent = self.reactionOverlayContainer self.forwardButton.isImplicitlyDisabled = true self.shareButton.isImplicitlyDisabled = true - self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), forControlEvents: .touchUpInside) - self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), forControlEvents: .touchUpInside) - self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), forControlEvents: .touchUpInside) - self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), forControlEvents: .touchUpInside) - self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), forControlEvents: .touchUpInside) - self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), forControlEvents: .touchUpInside) + self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside) + self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside) + self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside) + self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside) + self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside) + self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside) } deinit { @@ -187,19 +292,6 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { public func updateTheme(theme: PresentationTheme) { if self.theme !== theme { self.theme = theme - - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.tagButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/WebpageIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.tagEditButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/LinkSettingsIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - - self.separatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor } } @@ -356,6 +448,12 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { override public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded) + var leftInset = leftInset + leftInset += 8.0 + + var rightInset = rightInset + rightInset += 8.0 + let panelHeight = defaultHeight(metrics: metrics) if self.presentationInterfaceState != interfaceState { @@ -419,14 +517,14 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { width -= additionalSideInsets.right } - var tagButton: HighlightableButtonNode? + var tagButton: GlassButtonView? if !self.tagButton.isHidden { tagButton = self.tagButton } else if !self.tagEditButton.isHidden { tagButton = self.tagEditButton } - let buttons: [HighlightableButtonNode] + let buttons: [GlassButtonView] if self.reportButton.isHidden { if let tagButton { buttons = [ @@ -478,24 +576,25 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } } - let buttonSize = CGSize(width: 57.0, height: panelHeight) + let buttonSize = CGSize(width: 40.0, height: 40.0) let availableWidth = width - leftInset - rightInset let spacing: CGFloat = floor((availableWidth - buttonSize.width * CGFloat(buttons.count)) / CGFloat(buttons.count - 1)) var offset: CGFloat = leftInset for i in 0 ..< buttons.count { let button = buttons[i] + let buttonFrame: CGRect if i == buttons.count - 1 { - button.frame = CGRect(origin: CGPoint(x: width - rightInset - buttonSize.width, y: 0.0), size: buttonSize) + buttonFrame = CGRect(origin: CGPoint(x: width - rightInset - buttonSize.width, y: 0.0), size: buttonSize) } else { - button.frame = CGRect(origin: CGPoint(x: offset, y: 0.0), size: buttonSize) + buttonFrame = CGRect(origin: CGPoint(x: offset, y: 0.0), size: buttonSize) } + transition.updateFrame(view: button, frame: buttonFrame) + button.update(theme: interfaceState.theme, size: buttonFrame.size, transition: ComponentTransition(transition)) + offset += buttonSize.width + spacing } - transition.updateAlpha(node: self.separatorNode, alpha: isSecondary ? 1.0 : 0.0) - self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: panelHeight), size: CGSize(width: width, height: UIScreenPixel)) - if let reactionContextNode = self.reactionOverlayContainer.reactionContextNode, let tagButton { let isFirstTime = reactionContextNode.bounds.isEmpty diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 45247ccfdc..1100f5cdf7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -1434,7 +1434,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } else { self.sendAsAvatarNode.isHidden = true } - if mediaRecordingState != nil { + if mediaRecordingState != nil || interfaceState.interfaceState.mediaDraftState != nil { hasMenuButton = false } @@ -1449,7 +1449,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if hasMenuButton { hideOffset = CGPoint(x: width, y: 0.0) } else { - hideOffset = CGPoint(x: 0.0, y: 80.0) + hideOffset = CGPoint(x: 0.0, y: 80.0 + 60.0) } if self.startButton.isHidden { self.startButton.isHidden = false @@ -1885,11 +1885,14 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg panelHeight += 27.0 } - let menuButtonOriginY: CGFloat + var menuButtonOriginY: CGFloat if displayBotStartButton { menuButtonOriginY = floorToScreenPixels((minimalHeight - menuButtonHeight) / 2.0) } else { menuButtonOriginY = panelHeight - minimalHeight + floorToScreenPixels((minimalHeight - menuButtonHeight) / 2.0) + if accessoryPanel != nil { + menuButtonOriginY += 52.0 + } } let menuButtonFrame = CGRect(x: leftInset + 8.0, y: menuButtonOriginY, width: menuButtonExpanded ? menuButtonWidth : menuCollapsedButtonWidth, height: menuButtonHeight) @@ -1937,6 +1940,28 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.actionButtons.micButton.updateMode(mode: interfaceState.interfaceState.mediaRecordingMode, animated: transition.isAnimated) + var hideMicButton = false + if inputHasText || self.extendedSearchLayout { + hideMicButton = true + } + if let mediaRecordingState { + switch mediaRecordingState { + case .audio: + break + case let .video(status, _): + switch status { + case .recording: + break + case .editing: + hideMicButton = true + } + case .waitingForPreview: + break + } + } + + self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: transition.isAnimated) + var actionButtonsSize = CGSize(width: 40.0, height: 40.0) if let presentationInterfaceState = self.presentationInterfaceState { var showTitle = false @@ -1955,7 +1980,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg var textFieldInsets = self.textFieldInsets(metrics: metrics) if actionButtonsSize.width > 40.0 { - textFieldInsets.right = actionButtonsSize.width - 2.0 + textFieldInsets.right = actionButtonsSize.width + 14.0 } if additionalSideInsets.right > 0.0 { textFieldInsets.right += additionalSideInsets.right / 3.0 @@ -1967,12 +1992,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg textFieldInsets.left = 8.0 } - var hideMicButton = false var audioRecordingItemsAlpha: CGFloat = 1.0 if interfaceState.interfaceState.mediaDraftState != nil { audioRecordingItemsAlpha = 0.0 } - if mediaRecordingState != nil { + if let mediaRecordingState { audioRecordingItemsAlpha = 0.0 let audioRecordingInfoContainerNode: ASDisplayNode @@ -1993,7 +2017,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.audioRecordingTimeNode = audioRecordingTimeNode audioRecordingInfoContainerNode.addSubnode(audioRecordingTimeNode) - if transition.isAnimated && mediaRecordingState != nil { + if transition.isAnimated { animateTimeSlideIn = true } } @@ -2003,7 +2027,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if let currentAudioRecordingCancelIndicator = self.audioRecordingCancelIndicator { audioRecordingCancelIndicator = currentAudioRecordingCancelIndicator } else { - animateCancelSlideIn = transition.isAnimated && mediaRecordingState != nil + animateCancelSlideIn = transition.isAnimated audioRecordingCancelIndicator = ChatTextInputAudioRecordingCancelIndicator(theme: interfaceState.theme, strings: interfaceState.strings, cancel: { [weak self] in self?.viewOnce = false @@ -2014,38 +2038,35 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.textInputContainerBackgroundView.contentView.addSubview(audioRecordingCancelIndicator) } - let isLocked = mediaRecordingState?.isLocked ?? (interfaceState.interfaceState.mediaDraftState != nil) + let isLocked = mediaRecordingState.isLocked var hideInfo = false - if let mediaRecordingState = mediaRecordingState { - switch mediaRecordingState { - case let .audio(recorder, isLocked): - let hadAudioRecorder = self.actionButtons.micButton.audioRecorder != nil - if !hadAudioRecorder, isLocked { - self.actionButtons.micButton.lock() - } - self.actionButtons.micButton.audioRecorder = recorder - audioRecordingTimeNode.audioRecorder = recorder - case let .video(status, _): - let hadVideoRecorder = self.actionButtons.micButton.videoRecordingStatus != nil - if !hadVideoRecorder, isLocked { - self.actionButtons.micButton.lock() - } - switch status { - case let .recording(recordingStatus): - audioRecordingTimeNode.videoRecordingStatus = recordingStatus - self.actionButtons.micButton.videoRecordingStatus = recordingStatus - case .editing: - audioRecordingTimeNode.videoRecordingStatus = nil - self.actionButtons.micButton.videoRecordingStatus = nil - hideMicButton = true - hideInfo = true - } - case .waitingForPreview: - Queue.mainQueue().after(0.5, { - self.actionButtons.micButton.audioRecorder = nil - }) + switch mediaRecordingState { + case let .audio(recorder, isLocked): + let hadAudioRecorder = self.actionButtons.micButton.audioRecorder != nil + if !hadAudioRecorder, isLocked { + self.actionButtons.micButton.lock() } + self.actionButtons.micButton.audioRecorder = recorder + audioRecordingTimeNode.audioRecorder = recorder + case let .video(status, _): + let hadVideoRecorder = self.actionButtons.micButton.videoRecordingStatus != nil + if !hadVideoRecorder, isLocked { + self.actionButtons.micButton.lock() + } + switch status { + case let .recording(recordingStatus): + audioRecordingTimeNode.videoRecordingStatus = recordingStatus + self.actionButtons.micButton.videoRecordingStatus = recordingStatus + case .editing: + audioRecordingTimeNode.videoRecordingStatus = nil + self.actionButtons.micButton.videoRecordingStatus = nil + hideInfo = true + } + case .waitingForPreview: + Queue.mainQueue().after(0.5, { + self.actionButtons.micButton.audioRecorder = nil + }) } transition.updateAlpha(layer: self.textInputBackgroundNode.layer, alpha: 0.0) @@ -2075,7 +2096,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg audioRecordingCancelIndicator.layer.animatePosition(from: CGPoint(x: width + audioRecordingCancelIndicator.bounds.size.width, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) } - audioRecordingCancelIndicator.updateIsDisplayingCancel(isLocked, animated: !animateCancelSlideIn && mediaRecordingState != nil) + audioRecordingCancelIndicator.updateIsDisplayingCancel(isLocked, animated: !animateCancelSlideIn) if isLocked || self.actionButtons.micButton.cancelTranslation > cancelTransformThreshold { var deltaOffset: CGFloat = 0.0 @@ -2229,12 +2250,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } self.updateCounterTextNode(transition: transition) - - if inputHasText || self.extendedSearchLayout { - hideMicButton = true - } - - self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: transition.isAnimated) var textInputViewRealInsets = UIEdgeInsets() if let presentationInterfaceState = self.presentationInterfaceState { diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 7e44a174bd..88d6c0083e 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -286,6 +286,7 @@ public class GlassBackgroundView: UIView { let glassEffect = UIGlassEffect(style: .clear) glassEffect.isInteractive = false let nativeView = UIVisualEffectView(effect: glassEffect) + nativeView.layer.cornerCurve = .circular self.nativeView = nativeView nativeView.overrideUserInterfaceStyle = .light nativeView.traitOverrides.userInterfaceStyle = .light @@ -382,7 +383,7 @@ public class GlassBackgroundView: UIView { let glassEffect = UIGlassEffect(style: .clear) switch tintColor.kind { case .panel: - glassEffect.tintColor = tintColor.color + glassEffect.tintColor = tintColor.color.withMultipliedAlpha(1.2) case .custom: glassEffect.tintColor = tintColor.color } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 9d5a62e0d5..52f0cd6fb7 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -290,7 +290,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { private let shareMessages: () -> Void private let forwardMessages: () -> Void private let reportMessages: () -> Void - private let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void + private let displayCopyProtectionTip: (UIView, Bool) -> Void let selectionPanel: ChatMessageSelectionInputPanelNode let separatorNode: ASDisplayNode @@ -300,7 +300,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { return self.selectionPanel.viewForOverlayContent } - init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void) { + init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (UIView, Bool) -> Void) { self.context = context self.peerId = peerId self.deleteMessages = deleteMessages @@ -420,8 +420,8 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, openInviteRequests: { }, openSendAsPeer: { _, _ in }, presentChatRequestAdminInfo: { - }, displayCopyProtectionTip: { node, save in - displayCopyProtectionTip(node, save) + }, displayCopyProtectionTip: { view, save in + displayCopyProtectionTip(view, save) }, openWebView: { _, _, _, _ in }, updateShowWebView: { _ in }, insertText: { _ in @@ -12645,7 +12645,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.context.sharedContext.makeContentReportScreen(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), forceDark: false, present: { [weak self] controller in self?.controller?.push(controller) }, completion: {}, requestSelectMessages: nil) - }, displayCopyProtectionTip: { [weak self] node, save in + }, displayCopyProtectionTip: { [weak self] sourceView, save in if let strongSelf = self, let peer = strongSelf.data?.peer, let messageIds = strongSelf.state.selectedMessageIds, !messageIds.isEmpty { let _ = (strongSelf.context.engine.data.get(EngineDataMap( messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init) @@ -12707,7 +12707,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } strongSelf.controller?.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { if let strongSelf = self { - let rect = node.view.convert(node.view.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) + let rect = sourceView.convert(sourceView.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) return (strongSelf, rect) } return nil diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 053d08a7de..3e97459e39 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -928,7 +928,6 @@ public class VideoMessageCameraScreen: ViewController { self.backgroundColor = .clear - self.view.addSubview(self.backgroundView) self.view.addSubview(self.containerView) self.containerView.addSubview(self.previewContainerView) @@ -963,6 +962,7 @@ public class VideoMessageCameraScreen: ViewController { deinit { self.cameraStateDisposable?.dispose() self.idleTimerExtensionDisposable.dispose() + self.backgroundView.removeFromSuperview() } func withReadyCamera(isFirstTime: Bool = false, _ f: @escaping () -> Void) { @@ -1068,10 +1068,6 @@ public class VideoMessageCameraScreen: ViewController { func animateIn() { self.animatingIn = true -// if let chatNode = self.controller?.chatNode { -// chatNode.supernode?.view.insertSubview(self.backgroundView, aboveSubview: chatNode.view) -// } - self.backgroundView.alpha = 0.0 UIView.animate(withDuration: 0.4, animations: { self.backgroundView.alpha = 1.0 @@ -1430,12 +1426,13 @@ public class VideoMessageCameraScreen: ViewController { } var backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: controller.inputPanelFrame.0.minY)) + let actualBackgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height)) if backgroundFrame.maxY < layout.size.height - 100.0 && (layout.inputHeight ?? 0.0).isZero && !controller.inputPanelFrame.1 && layout.additionalInsets.bottom.isZero { backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height - layout.intrinsicInsets.bottom - controller.inputPanelFrame.0.height)) } - transition.setPosition(view: self.backgroundView, position: backgroundFrame.center) - transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) + transition.setPosition(view: self.backgroundView, position: actualBackgroundFrame.center) + transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: actualBackgroundFrame.size)) transition.setPosition(view: self.containerView, position: backgroundFrame.center) transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) @@ -1637,6 +1634,10 @@ public class VideoMessageCameraScreen: ViewController { public var onResume: () -> Void = { } + public var backgroundView: UIVisualEffectView { + return self.node.backgroundView + } + public struct RecordedVideoData { public let duration: Double public let frames: [UIImage] diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 23a1755dda..f91e1991c2 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -3881,7 +3881,7 @@ extension ChatControllerImpl { let cleanInsets = layout.intrinsicInsets let insets = layout.insets(options: .input) - let bottomInset = max(insets.bottom, cleanInsets.bottom) + 43.0 + let bottomInset = max(insets.bottom, cleanInsets.bottom) + 54.0 let defaultMyPeerId: PeerId if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) { @@ -3932,7 +3932,7 @@ extension ChatControllerImpl { }) }, presentChatRequestAdminInfo: { [weak self] in self?.presentChatRequestAdminInfo() - }, displayCopyProtectionTip: { [weak self] node, save in + }, displayCopyProtectionTip: { [weak self] sourceView, save in if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer, let messageIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds { let _ = (strongSelf.context.engine.data.get(EngineDataMap( messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init) @@ -3994,7 +3994,7 @@ extension ChatControllerImpl { } strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { if let strongSelf = self { - let rect = node.view.convert(node.view.bounds, to: strongSelf.chatDisplayNode.view).offsetBy(dx: 0.0, dy: 3.0) + let rect = sourceView.convert(sourceView.bounds, to: strongSelf.chatDisplayNode.view).offsetBy(dx: 0.0, dy: 3.0) return (strongSelf.chatDisplayNode, rect) } return nil diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 44ee4a95d6..e0d412926b 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5676,7 +5676,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - if let videoRecorder = videoRecorder { + if let videoRecorder { strongSelf.recorderFeedback?.impact(.light) videoRecorder.onStop = { @@ -5684,6 +5684,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.dismissMediaRecorder(.pause) } } + strongSelf.chatDisplayNode.setVideoRecorder(backgroundView: videoRecorder.backgroundView) strongSelf.present(videoRecorder, in: .window(.root)) if strongSelf.lockMediaRecordingRequestId == strongSelf.beginMediaRecordingRequestId { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 123c87a976..32e661f07c 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -5095,10 +5095,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { inputPanelNodeSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputPanelNodeSnapshot] _ in inputPanelNodeSnapshot?.removeFromSuperview() }) - inputPanelNodeSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -5.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) - inputPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - inputPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 5.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) } } @@ -5262,4 +5259,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { ) } } + + func setVideoRecorder(backgroundView: UIView?) { + if let backgroundView { + self.wrappingNode.contentNode.view.insertSubview(backgroundView, belowSubview: self.inputPanelContainerNode.view) + } + } } diff --git a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift index 17eeec3fee..e8f743f52c 100644 --- a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift @@ -16,6 +16,7 @@ import PlainButtonComponent import ComponentDisplayAdapters import BundleIconComponent import AnimatedTextComponent +import GlassBackgroundComponent private let labelFont = Font.regular(15.0) @@ -58,6 +59,8 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } + private let leftControlsBackgroundView: GlassBackgroundView + private let rightControlsBackgroundView: GlassBackgroundView private let calendarButton = ComponentView() private var membersButton: ComponentView? private var resultsText: ComponentView? @@ -90,7 +93,13 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { init(theme: PresentationTheme, alwaysShowTotalMessagesCount: Bool) { self.alwaysShowTotalMessagesCount = alwaysShowTotalMessagesCount + self.leftControlsBackgroundView = GlassBackgroundView() + self.rightControlsBackgroundView = GlassBackgroundView() + super.init() + + self.view.addSubview(self.leftControlsBackgroundView) + self.view.addSubview(self.rightControlsBackgroundView) } deinit { @@ -282,6 +291,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { let size = CGSize(width: params.width - params.additionalSideInsets.left * 2.0 - params.leftInset * 2.0, height: height) + var listModeButtonFrameValue: CGRect? if canChangeListMode { var listModeButtonTransition = transition let listModeButton: ComponentView @@ -298,11 +308,11 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { component: AnyComponent(PlainButtonComponent( content: AnyComponent(AnimatedTextComponent( font: Font.regular(15.0), - color: params.interfaceState.theme.rootController.navigationBar.accentTextColor, + color: params.interfaceState.theme.chat.inputPanel.inputControlColor, items: modeButtonTitle )), effectAlignment: .right, - minSize: CGSize(width: 1.0, height: size.height), + minSize: CGSize(width: 1.0, height: 40.0), contentInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), action: { [weak self] in guard let self, let params = self.currentLayout?.params else { @@ -322,7 +332,8 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { buttonView.alpha = 0.0 self.view.addSubview(buttonView) } - let listModeFrame = CGRect(origin: CGPoint(x: size.width - params.rightInset - 11.0 - buttonSize.width, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize) + let listModeFrame = CGRect(origin: CGPoint(x: size.width - params.rightInset - 20.0 - 8.0 - buttonSize.width, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize) + listModeButtonFrameValue = listModeFrame listModeButtonTransition.setPosition(view: buttonView, position: CGPoint(x: listModeFrame.minX + listModeFrame.width * buttonView.layer.anchorPoint.x, y: listModeFrame.minY + listModeFrame.height * buttonView.layer.anchorPoint.y)) listModeButtonTransition.setBounds(view: buttonView, bounds: CGRect(origin: CGPoint(), size: listModeFrame.size)) transition.setAlpha(view: buttonView, alpha: 1.0) @@ -338,20 +349,23 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } - var nextLeftX: CGFloat = 16.0 + var nextLeftX: CGFloat = 16.0 + 8.0 + + var calendarButtonFrameValue: CGRect? + var membersButtonFrameValue: CGRect? + var resultsTextFrameValue: CGRect? if !self.alwaysShowTotalMessagesCount && self.externalSearchResultsCount == nil { - nextLeftX = 12.0 + nextLeftX -= 4.0 let calendarButtonSize = self.calendarButton.update( transition: .immediate, component: AnyComponent(PlainButtonComponent( content: AnyComponent(BundleIconComponent( name: "Chat/Input/Search/Calendar", - tintColor: params.interfaceState.theme.rootController.navigationBar.accentTextColor + tintColor: params.interfaceState.theme.chat.inputPanel.inputControlColor )), effectAlignment: .center, - minSize: CGSize(width: 1.0, height: size.height), - contentInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), + minSize: CGSize(width: 40.0, height: 40.0), action: { [weak self] in guard let self else { return @@ -363,6 +377,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { containerSize: size ) let calendarButtonFrame = CGRect(origin: CGPoint(x: nextLeftX, y: floor((size.height - calendarButtonSize.height) * 0.5)), size: calendarButtonSize) + calendarButtonFrameValue = calendarButtonFrame if let calendarButtonView = self.calendarButton.view { if calendarButtonView.superview == nil { self.view.addSubview(calendarButtonView) @@ -375,7 +390,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } transition.setFrame(view: calendarButtonView, frame: calendarButtonFrame) } - nextLeftX += calendarButtonSize.width + 8.0 + nextLeftX += calendarButtonSize.width + 0.0 } else if let calendarButtonView = self.calendarButton.view { if transition.animation.isImmediate { calendarButtonView.removeFromSuperview() @@ -404,11 +419,10 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { component: AnyComponent(PlainButtonComponent( content: AnyComponent(BundleIconComponent( name: "Chat/Input/Search/Members", - tintColor: params.interfaceState.theme.rootController.navigationBar.accentTextColor + tintColor: params.interfaceState.theme.chat.inputPanel.inputControlColor )), effectAlignment: .center, - minSize: CGSize(width: 1.0, height: size.height), - contentInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), + minSize: CGSize(width: 40.0, height: 40.0), action: { [weak self] in guard let self else { return @@ -428,14 +442,16 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { animateIn = true self.view.addSubview(buttonView) } - membersButtonTransition.setFrame(view: buttonView, frame: CGRect(origin: CGPoint(x: nextLeftX, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize)) + let membersButtonFrame = CGRect(origin: CGPoint(x: nextLeftX, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize) + membersButtonFrameValue = membersButtonFrame + membersButtonTransition.setFrame(view: buttonView, frame: membersButtonFrame) transition.setAlpha(view: buttonView, alpha: 1.0) if animateIn { transition.animateScale(view: buttonView, from: 0.001, to: 1.0) } } - nextLeftX += buttonSize.width + 8.0 + nextLeftX += buttonSize.width + 0.0 } else { if let membersButton = self.membersButton { self.membersButton = nil @@ -468,7 +484,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { component: AnyComponent(PlainButtonComponent( content: AnyComponent(AnimatedTextComponent( font: Font.regular(15.0), - color: (params.interfaceState.displayHistoryFilterAsList && canChangeListMode) ? params.interfaceState.theme.rootController.navigationBar.accentTextColor : params.interfaceState.theme.rootController.navigationBar.secondaryTextColor, + color: params.interfaceState.theme.rootController.navigationBar.secondaryTextColor, items: resultsTextString )), effectAlignment: .center, @@ -484,6 +500,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { containerSize: CGSize(width: 200.0, height: 100.0) ) let resultsTextFrame = CGRect(origin: CGPoint(x: nextLeftX - 3.0, y: floor((size.height - resultsTextSize.height) * 0.5)), size: resultsTextSize) + resultsTextFrameValue = resultsTextFrame if let resultsTextView = resultsText.view { if resultsTextView.superview == nil { resultsTextView.alpha = 0.0 @@ -503,6 +520,57 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } } + + let adjustedResultsTextFrameValue = resultsTextFrameValue.flatMap { rect in + var rect = rect + rect.size.width += 8.0 + return rect + } + + let leftControlsFrames: [CGRect?] = [ + calendarButtonFrameValue, + membersButtonFrameValue, + adjustedResultsTextFrameValue + ] + var leftControlsRect = CGRect() + for rect in leftControlsFrames { + guard let rect else { + continue + } + if leftControlsRect.isEmpty { + leftControlsRect = rect + } else { + leftControlsRect = leftControlsRect.union(rect) + } + } + + var leftControlsBackgroundFrame = CGRect(origin: CGPoint(x: 20.0, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) + leftControlsBackgroundFrame.size.width = max(40.0, leftControlsRect.maxX - leftControlsBackgroundFrame.minX) + transition.setFrame(view: self.leftControlsBackgroundView, frame: leftControlsBackgroundFrame) + self.leftControlsBackgroundView.update(size: leftControlsBackgroundFrame.size, cornerRadius: leftControlsBackgroundFrame.height * 0.5, isDark: params.interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition) + transition.setAlpha(view: self.leftControlsBackgroundView, alpha: leftControlsRect.isEmpty ? 0.0 : 1.0) + + let rightControlsFrames: [CGRect?] = [ + listModeButtonFrameValue + ] + var rightControlsRect = CGRect() + for rect in rightControlsFrames { + guard let rect else { + continue + } + if rightControlsRect.isEmpty { + rightControlsRect = rect + } else { + rightControlsRect = rightControlsRect.union(rect) + } + } + + var rightControlsBackgroundFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 20.0, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) + rightControlsBackgroundFrame.size.width = max(40.0, rightControlsRect.maxX - rightControlsRect.minX + 8.0 * 2.0) + rightControlsBackgroundFrame.origin.x -= rightControlsBackgroundFrame.width + transition.setFrame(view: self.rightControlsBackgroundView, frame: rightControlsBackgroundFrame) + self.rightControlsBackgroundView.update(size: rightControlsBackgroundFrame.size, cornerRadius: rightControlsBackgroundFrame.height * 0.5, isDark: params.interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition) + transition.setAlpha(view: self.rightControlsBackgroundView, alpha: rightControlsRect.isEmpty ? 0.0 : 1.0) return height } diff --git a/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift index c06faaa585..bc1d943b92 100644 --- a/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift @@ -16,6 +16,7 @@ import ChatInputContextPanelNode import ComponentFlow import ComponentDisplayAdapters import GlassBackgroundComponent +import EdgeEffect private struct CommandMenuChatInputContextPanelEntryStableId: Hashable { let command: PeerCommand @@ -66,6 +67,7 @@ private func preparedTransition(from fromEntries: [CommandMenuChatInputContextPa final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { private let backgroundView: GlassBackgroundView private let listView: ListView + private let listMaskView: UIImageView private var currentEntries: [CommandMenuChatInputContextPanelEntry]? private var enqueuedTransitions: [(CommandMenuChatInputContextPanelTransition, Bool)] = [] @@ -87,6 +89,8 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { return strings.VoiceOver_ScrollStatus(row, count).string } + self.listMaskView = UIImageView() + super.init(context: context, theme: theme, strings: strings, fontSize: fontSize, chatPresentationContext: chatPresentationContext) self.isOpaque = false @@ -94,6 +98,7 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { self.view.addSubview(self.backgroundView) self.addSubnode(self.listView) + self.listView.view.mask = self.listMaskView self.backgroundView.isHidden = true self.listView.visibleContentOffsetChanged = { [weak self] offset in @@ -240,6 +245,15 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { let hadValidLayout = self.validLayout != nil self.validLayout = (size, leftInset, rightInset, bottomInset) + self.backgroundView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + 32.0)) + self.backgroundView.update( + size: self.backgroundView.bounds.size, + cornerRadius: 20.0, + isDark: interfaceState.theme.overallDarkAppearance, + tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), + transition: ComponentTransition(transition) + ) + var insets = UIEdgeInsets() insets.top = self.topInsetForLayout(size: size, bottomInset: bottomInset) insets.left = leftInset @@ -247,6 +261,37 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { insets.bottom = bottomInset transition.updateFrame(node: self.listView, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) + let listMaskHeight: CGFloat = bottomInset + 1.0 + if self.listMaskView.image?.size.height != listMaskHeight { + let baseGradientAlpha: CGFloat = 0.65 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.0 + let colors: [UIColor] = (0 ..< numSteps).map { i in + if i < firstStep { + return UIColor(white: 0.0, alpha: 0.0) + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 0.0, alpha: 1.0 - baseGradientAlpha * value) + } + } + let locations: [CGFloat] = (0 ..< numSteps).map { i in + if i < firstStep { + return 0.0 + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) + } + } + + self.listMaskView.image = generateGradientImage( + size: CGSize(width: 8.0, height: listMaskHeight), + colors: colors, + locations: locations + )?.stretchableImage(withLeftCapWidth: 0, topCapHeight: 1) + } + transition.updateFrame(view: self.listMaskView, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: curve) diff --git a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputContextPanelNode.swift index 78903e8874..2d82dd94ce 100644 --- a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputContextPanelNode.swift @@ -163,7 +163,6 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex self.view.addSubview(self.backgroundView) self.addSubnode(self.listView) - //self.view.addSubview(self.listMaskView) self.listView.view.mask = self.listMaskView self.listView.visibleBottomContentOffsetChanged = { [weak self] offset in