diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index b4c8c28192..022076b735 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -17,6 +17,10 @@ public protocol PagerContentViewWithBackground: UIView { func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, externalTintMaskContainer: UIView?, transition: ComponentTransition) } +public protocol PagerTopPanelView: UIView { + var tintContentMask: UIView { get } +} + public final class PagerComponentChildEnvironment: Equatable { public struct ContentScrollingUpdate { public var relativeOffset: CGFloat @@ -671,6 +675,12 @@ public final class PagerComponent() self.tintMaskAnimationView = UIImageView() default: @@ -87,13 +88,9 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie } self.iconImageView.image = image - if #available(iOS 26.0, *) { - self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor.withAlphaComponent(1.0) - self.iconImageView.alpha = alpha * theme.chat.inputPanel.inputControlColor.alpha - } else { - self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor - self.iconImageView.alpha = alpha - } + self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor.withAlphaComponent(1.0) + self.iconImageView.alpha = alpha * theme.chat.inputPanel.inputControlColor.alpha + self.iconImageView.tintMask.alpha = alpha * theme.chat.inputPanel.inputControlColor.alpha self.accessibilityLabel = accessibilityLabel @@ -133,13 +130,8 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie } self.iconImageView.image = image - if #available(iOS 26.0, *) { - self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor.withAlphaComponent(1.0) - self.iconImageView.alpha = alpha * theme.chat.inputPanel.inputControlColor.alpha - } else { - self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor - self.iconImageView.alpha = alpha - } + self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor.withAlphaComponent(1.0) + self.iconImageView.alpha = alpha * theme.chat.inputPanel.inputControlColor.alpha self.accessibilityLabel = accessibilityLabel } @@ -211,6 +203,7 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie imageFrame.origin.y += 1.0 } self.iconImageView.frame = imageFrame + self.iconImageView.tintMask.frame = imageFrame if let animationView = self.animationView { let width = AccessoryItemIconButton.calculateWidth(item: item, image: image, text: "", strings: self.strings) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index dc34fa8374..65482d65f6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -338,6 +338,8 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } } + public var ignoreInputStateUpdates: Bool = false + override public var context: AccountContext? { didSet { self.actionButtons.micButton.statusBarHost = self.context?.sharedContext.mainWindow?.statusBarHost @@ -362,6 +364,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } public func updateInputTextState(_ state: ChatTextInputState, keepSendButtonEnabled: Bool, extendedSearchLayout: Bool, accessoryItems: [ChatTextInputAccessoryItem], animated: Bool) { + if self.ignoreInputStateUpdates { + return + } + if let currentState = self.presentationInterfaceState { var updateAccessoryButtons = false if accessoryItems.count == self.accessoryItemButtons.count { @@ -1958,7 +1964,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } } - self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: transition.isAnimated) + self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, hideMicButtonBackground: mediaRecordingState != nil, animated: transition.isAnimated) var actionButtonsSize = CGSize(width: 40.0, height: 40.0) if let presentationInterfaceState = self.presentationInterfaceState { @@ -1995,8 +2001,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg audioRecordingItemsAlpha = 0.0 } - transition.updateAlpha(layer: self.actionButtons.micButtonBackgroundView.layer, alpha: mediaRecordingState != nil ? 0.01 : 1.0) - if let mediaRecordingState { audioRecordingItemsAlpha = 0.0 @@ -3675,7 +3679,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.updateTextHeight(animated: animated) } - private func updateActionButtons(hasText: Bool, hideMicButton: Bool, animated: Bool) { + private func updateActionButtons(hasText: Bool, hideMicButton: Bool, hideMicButtonBackground: Bool, animated: Bool) { var hideMicButton = hideMicButton var mediaInputIsActive = false @@ -3832,19 +3836,15 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } } + let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut) + if hideMicButton { if !self.actionButtons.micButton.alpha.isZero { - self.actionButtons.micButton.alpha = 0.0 - if animated { - self.actionButtons.micButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - } - - self.actionButtons.micButtonBackgroundView.alpha = 0.0 - if animated { - self.actionButtons.micButtonBackgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - } + alphaTransition.updateAlpha(layer: self.actionButtons.micButton.layer, alpha: 0.0) } + alphaTransition.updateAlpha(layer: self.actionButtons.micButtonBackgroundView.layer, alpha: 0.0) } else { + alphaTransition.updateAlpha(layer: self.actionButtons.micButtonBackgroundView.layer, alpha: hideMicButtonBackground ? 0.0 : 1.0) let micAlpha: CGFloat = self.actionButtons.micButton.fadeDisabled ? 0.5 : 1.0 if !self.actionButtons.micButton.alpha.isEqual(to: micAlpha) { self.actionButtons.micButton.alpha = micAlpha diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 0a98d73031..069339013f 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -169,7 +169,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { hasStickers: Bool = true, hasGifs: Bool = true, hideBackground: Bool = false, - maskEdge: Bool = false, + maskEdge: EmojiPagerContentComponent.MaskEdgeMode = .none, forceHasPremium: Bool = false, sendGif: ((FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool)? ) -> Signal { @@ -213,7 +213,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { hasEdit: hasEdit, hasAdd: hasEdit, subject: .chatStickers, - hideBackground: hideBackground + hideBackground: hideBackground, + maskEdge: maskEdge ) |> map(Optional.init) } else { @@ -1915,7 +1916,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { externalTopPanelContainer: self.externalTopPanelContainerImpl, externalBottomPanelContainer: nil, externalTintMaskContainer: self.backgroundTintMaskContentView, - displayTopPanelBackground: self.opaqueTopPanelBackground ? .opaque : .blur, + displayTopPanelBackground: self.opaqueTopPanelBackground ? .opaque : .none, topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Components/EntityKeyboard/BUILD b/submodules/TelegramUI/Components/EntityKeyboard/BUILD index 9160854d12..4aa7f411b7 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/BUILD +++ b/submodules/TelegramUI/Components/EntityKeyboard/BUILD @@ -52,6 +52,7 @@ swift_library( "//submodules/TelegramCore/FlatSerialization", "//submodules/TelegramUI/Components/BatchVideoRendering", "//submodules/TelegramUI/Components/GifVideoLayer", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 68c579f914..bb3da9d9c9 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -610,6 +610,12 @@ public final class EmojiPagerContentComponent: Component { } } + public enum MaskEdgeMode { + case none + case fade + case clip + } + public let id: AnyHashable public let context: AccountContext public let avatarPeer: EnginePeer? @@ -623,7 +629,7 @@ public final class EmojiPagerContentComponent: Component { public let searchState: SearchState public let warpContentsOnEdges: Bool public let hideBackground: Bool - public let maskEdge: Bool + public let maskEdge: MaskEdgeMode public let displaySearchWithPlaceholder: String? public let searchCategories: EmojiSearchCategories? public let searchInitiallyHidden: Bool @@ -649,7 +655,7 @@ public final class EmojiPagerContentComponent: Component { searchState: SearchState, warpContentsOnEdges: Bool, hideBackground: Bool, - maskEdge: Bool, + maskEdge: MaskEdgeMode, displaySearchWithPlaceholder: String?, searchCategories: EmojiSearchCategories?, searchInitiallyHidden: Bool, @@ -1383,6 +1389,9 @@ public final class EmojiPagerContentComponent: Component { private let backgroundView: BlurredBackgroundView private let backgroundTintView: UIView private var fadingMaskLayer: FadingMaskLayer? + private var tintFadingMaskLayer: FadingMaskLayer? + private var topPanelSeparator: (colorLayer: SimpleLayer, tintLayer: SimpleLayer)? + private var topPanelHeight: CGFloat = 0.0 private var vibrancyClippingView: UIView private var vibrancyEffectView: UIView? public private(set) var mirrorContentClippingView: UIView? @@ -4007,6 +4016,26 @@ public final class EmojiPagerContentComponent: Component { if let fadingMaskLayer = self.fadingMaskLayer { fadingMaskLayer.internalAlpha = max(0.0, min(1.0, self.scrollView.contentOffset.y / 30.0)) } + if let tintFadingMaskLayer = self.tintFadingMaskLayer { + tintFadingMaskLayer.internalAlpha = max(0.0, min(1.0, self.scrollView.contentOffset.y / 30.0)) + + self.updateTopPanelSeparator(transition: transition) + } + } + + private func updateTopPanelSeparator(transition: ComponentTransition) { + if let topPanelSeparator = self.topPanelSeparator { + var offset = self.scrollView.contentOffset.y + let startOffset: CGFloat = 40.0 - self.topPanelHeight + let endOffset: CGFloat = startOffset + 10.0 + + offset = min(max(offset, startOffset), endOffset) + offset = (endOffset - offset) / (endOffset - startOffset) + let alpha = 1.0 - offset + + transition.setAlpha(layer: topPanelSeparator.colorLayer, alpha: alpha) + transition.setAlpha(layer: topPanelSeparator.tintLayer, alpha: alpha) + } } private func updateShimmerIfNeeded() { @@ -4075,18 +4104,58 @@ public final class EmojiPagerContentComponent: Component { if component.hideBackground { self.backgroundView.isHidden = true - if component.maskEdge { + if component.maskEdge != .none { let maskLayer: FadingMaskLayer if let current = self.fadingMaskLayer { maskLayer = current } else { - maskLayer = FadingMaskLayer() + maskLayer = FadingMaskLayer(isHard: component.maskEdge == .clip) self.fadingMaskLayer = maskLayer } + + let tintFadingMaskLayer: FadingMaskLayer + if let current = self.tintFadingMaskLayer { + tintFadingMaskLayer = current + } else { + tintFadingMaskLayer = FadingMaskLayer(isHard: component.maskEdge == .clip) + self.tintFadingMaskLayer = tintFadingMaskLayer + } + + if case .clip = component.maskEdge { + let topPanelSeparator: (colorLayer: SimpleLayer, tintLayer: SimpleLayer) + if let current = self.topPanelSeparator { + topPanelSeparator = current + } else { + topPanelSeparator = (SimpleLayer(), SimpleLayer()) + self.topPanelSeparator = topPanelSeparator + self.layer.addSublayer(topPanelSeparator.colorLayer) + if let effectContainerView = externalTintMaskContainer ?? component.inputInteractionHolder.inputInteraction?.externalBackground?.effectContainerView { + effectContainerView.layer.addSublayer(topPanelSeparator.tintLayer) + } + } + + topPanelSeparator.colorLayer.backgroundColor = keyboardChildEnvironment.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5).cgColor + topPanelSeparator.tintLayer.backgroundColor = UIColor(white: 0.0, alpha: 0.7).cgColor + + let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - UIScreenPixel), size: CGSize(width: backgroundFrame.width, height: UIScreenPixel)) + transition.setFrame(layer: topPanelSeparator.colorLayer, frame: separatorFrame) + transition.setFrame(layer: topPanelSeparator.tintLayer, frame: separatorFrame) + + self.topPanelHeight = topPanelHeight + self.updateTopPanelSeparator(transition: transition) + } + if self.layer.mask == nil { self.layer.mask = maskLayer } - maskLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((topPanelHeight - 34.0) * 0.75)), size: backgroundFrame.size) + if self.mirrorContentClippingView?.layer.mask != tintFadingMaskLayer { + self.mirrorContentClippingView?.layer.mask = tintFadingMaskLayer + } + let maskFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 40.0), size: backgroundFrame.size) + transition.setFrame(layer: maskLayer, frame: maskFrame) + transition.setFrame(layer: tintFadingMaskLayer, frame: maskLayer.frame) + maskLayer.update(size: maskFrame.size, transition: transition) + tintFadingMaskLayer.update(size: maskFrame.size, transition: transition) } } else if component.warpContentsOnEdges { self.backgroundView.isHidden = true @@ -4910,18 +4979,38 @@ private final class FadingMaskLayer: SimpleLayer { let fillLayer = SimpleLayer() let gradientFillLayer = SimpleLayer() + private let isHard: Bool + var internalAlpha: CGFloat = 1.0 { didSet { self.gradientFillLayer.opacity = Float(1.0 - self.internalAlpha) } } - override func layoutSublayers() { + init(isHard: Bool) { + self.isHard = isHard + super.init() + } + + override init(layer: Any) { + self.isHard = false + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, transition: ComponentTransition) { let gradientHeight: CGFloat = 66.0 if self.gradientLayer.contents == nil { - self.addSublayer(self.gradientLayer) + if !self.isHard { + self.addSublayer(self.gradientLayer) + } self.addSublayer(self.fillLayer) - self.addSublayer(self.gradientFillLayer) + if !self.isHard { + self.addSublayer(self.gradientFillLayer) + } let gradientImage = generateGradientImage(size: CGSize(width: 1.0, height: gradientHeight), colors: [UIColor.white.withAlphaComponent(0.0), UIColor.white.withAlphaComponent(0.0), UIColor.white, UIColor.white], locations: [0.0, 0.4, 0.9, 1.0], direction: .vertical) self.gradientLayer.contents = gradientImage?.cgImage @@ -4930,9 +5019,14 @@ private final class FadingMaskLayer: SimpleLayer { self.gradientFillLayer.backgroundColor = UIColor.white.cgColor } - self.gradientLayer.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: gradientHeight)) - self.gradientFillLayer.frame = self.gradientLayer.frame - self.fillLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: gradientHeight), size: CGSize(width: self.bounds.width, height: self.bounds.height - gradientHeight)) + transition.setFrame(layer: self.gradientLayer, frame: CGRect(origin: .zero, size: CGSize(width: size.width, height: gradientHeight))) + transition.setFrame(layer: self.gradientFillLayer, frame: self.gradientLayer.frame) + if self.isHard { + let hardHeight: CGFloat = 40.0 + transition.setFrame(layer: self.fillLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: hardHeight), size: CGSize(width: size.width, height: size.height - hardHeight))) + } else { + transition.setFrame(layer: self.fillLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: gradientHeight), size: CGSize(width: size.width, height: size.height - gradientHeight))) + } } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift index c16e3501b5..df4dd2efbb 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift @@ -66,7 +66,7 @@ public extension EmojiPagerContentComponent { forceHasPremium: Bool = false, premiumIfSavedMessages: Bool = true, hideBackground: Bool = false, - maskEdge: Bool = false + maskEdge: EmojiPagerContentComponent.MaskEdgeMode = .none ) -> Signal { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let isPremiumDisabled = premiumConfiguration.isPremiumDisabled @@ -1635,7 +1635,7 @@ public extension EmojiPagerContentComponent { searchIsPlaceholderOnly: Bool = true, subject: StickersSubject = .chatStickers, hideBackground: Bool = false, - maskEdge: Bool = false + maskEdge: EmojiPagerContentComponent.MaskEdgeMode = .none ) -> Signal { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let isPremiumDisabled = premiumConfiguration.isPremiumDisabled @@ -2202,7 +2202,7 @@ public extension EmojiPagerContentComponent { animationRenderer: MultiAnimationRenderer, hasSearch: Bool, hideBackground: Bool = false, - maskEdge: Bool = false + maskEdge: EmojiPagerContentComponent.MaskEdgeMode = .none ) -> Signal { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let isPremiumDisabled = premiumConfiguration.isPremiumDisabled diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift index 1366bfafd4..3967917a8b 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift @@ -469,7 +469,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode searchState: .empty(hasResults: false), warpContentsOnEdges: false, hideBackground: false, - maskEdge: false, + maskEdge: .none, displaySearchWithPlaceholder: self.presentationData.strings.EmojiSearch_SearchEmojiPlaceholder, searchCategories: nil, searchInitiallyHidden: false, diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift index 28d65064f6..a7d0dfc927 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift @@ -72,11 +72,12 @@ final class EntityKeyboardTopContainerPanelComponent: Component { private final class PanelView { let view = ComponentHostView() + let tintContentView = UIView() let visibilityFractionUpdated = ActionSlot<(CGFloat, ComponentTransition)>() var isExpanded: Bool = false } - final class View: UIView { + final class View: UIView, PagerTopPanelView { private var backgroundView: BlurredBackgroundView? private var backgroundSeparatorView: UIView? @@ -88,7 +89,11 @@ final class EntityKeyboardTopContainerPanelComponent: Component { private var visibilityFraction: CGFloat = 1.0 + public let tintContentMask: UIView + override init(frame: CGRect) { + self.tintContentMask = UIView() + super.init(frame: frame) self.disablesInteractiveKeyboardGestureRecognizer = true @@ -150,6 +155,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component { panelView = PanelView() self.panelViews[panel.id] = panelView self.addSubview(panelView.view) + self.tintContentMask.addSubview(panelView.tintContentView) } let panelId = panel.id @@ -172,12 +178,20 @@ final class EntityKeyboardTopContainerPanelComponent: Component { ) if isInBounds { transition.animatePosition(view: panelView.view, from: CGPoint(x: transitionOffsetFraction * availableSize.width, y: 0.0), to: CGPoint(), additive: true, completion: nil) + transition.animatePosition(view: panelView.tintContentView, from: CGPoint(x: transitionOffsetFraction * availableSize.width, y: 0.0), to: CGPoint(), additive: true, completion: nil) } panelTransition.setFrame(view: panelView.view, frame: panelFrame, completion: { [weak self] completed in if isPartOfTransition && completed { self?.state?.updated(transition: .immediate) } }) + panelTransition.setFrame(view: panelView.tintContentView, frame: panelFrame) + if let panelViewImpl = panelView.view.componentView as? PagerTopPanelView { + if panelViewImpl.tintContentMask.superview == nil { + panelView.tintContentView.addSubview(panelViewImpl.tintContentMask) + } + panelTransition.setFrame(view: panelViewImpl.tintContentMask, frame: CGRect(origin: CGPoint(), size: panelFrame.size)) + } } } } @@ -186,6 +200,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component { if !validPanelIds.contains(id) { removedPanelIds.append(id) panelView.view.removeFromSuperview() + panelView.tintContentView.removeFromSuperview() } } for id in removedPanelIds { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 59e9034eb2..d6c9379c2e 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -13,6 +13,7 @@ import AccountContext import MultilineTextComponent import LottieAnimationComponent import AvatarNode +import GlassBackgroundComponent final class EntityKeyboardAnimationTopPanelComponent: Component { typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment @@ -324,12 +325,16 @@ final class EntityKeyboardIconTopPanelComponent: Component { } final class View: UIView { - let iconView: UIImageView + let iconView: GlassBackgroundView.ContentImageView + let tintMaskView: UIView + var component: EntityKeyboardIconTopPanelComponent? var titleView: ComponentView? override init(frame: CGRect) { - self.iconView = UIImageView() + self.tintMaskView = UIView() + self.iconView = GlassBackgroundView.ContentImageView() + self.tintMaskView.addSubview(self.iconView.tintMask) super.init(frame: frame) @@ -355,13 +360,13 @@ final class EntityKeyboardIconTopPanelComponent: Component { var image: UIImage? switch component.icon { case .featured: - image = UIImage(bundleImageName: "Chat/Input/Media/PanelFeaturedIcon") + image = UIImage(bundleImageName: "Chat/Input/Media/PanelFeaturedIcon")?.withRenderingMode(.alwaysTemplate) case .trending: - image = UIImage(bundleImageName: "Chat/Input/Media/PanelTrendingIcon") + image = UIImage(bundleImageName: "Chat/Input/Media/PanelTrendingIcon")?.withRenderingMode(.alwaysTemplate) case .recent: - image = UIImage(bundleImageName: "Chat/Input/Media/PanelRecentIcon") + image = UIImage(bundleImageName: "Chat/Input/Media/PanelRecentIcon")?.withRenderingMode(.alwaysTemplate) case .saved: - image = UIImage(bundleImageName: "Chat/Input/Media/PanelSavedIcon") + image = UIImage(bundleImageName: "Chat/Input/Media/PanelSavedIcon")?.withRenderingMode(.alwaysTemplate) case .liked: image = UIImage(bundleImageName: "Chat/Input/Media/PanelHeartIcon")?.withRenderingMode(.alwaysTemplate) case .collectible: @@ -402,10 +407,10 @@ final class EntityKeyboardIconTopPanelComponent: Component { if component.useAccentColor { color = component.theme.list.itemAccentColor } else { - color = component.theme.chat.inputMediaPanel.panelHighlightedIconColor + color = component.theme.chat.inputPanel.inputControlColor } } else { - color = component.theme.chat.inputMediaPanel.panelIconColor + color = component.theme.chat.inputPanel.inputControlColor } } @@ -1278,7 +1283,94 @@ public final class EntityKeyboardTopPanelComponent: Component { } } - public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView { + public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView, PagerTopPanelView { + private final class ContentScrollLayer: CALayer { + public var mirrorLayer: CALayer? + + override public init() { + super.init() + } + + override public init(layer: Any) { + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public var position: CGPoint { + get { + return super.position + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.position = value + } + super.position = value + } + } + + override public var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.bounds = value + } + super.bounds = value + } + } + + override public func add(_ animation: CAAnimation, forKey key: String?) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override public func removeAllAnimations() { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override public func removeAnimation(forKey: String) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } + } + + private final class ContentScrollView: UIScrollView, PagerExpandableScrollView { + override static var layerClass: AnyClass { + return ContentScrollLayer.self + } + + private let mirrorView: UIView + + init(mirrorView: UIView) { + self.mirrorView = mirrorView + + super.init(frame: CGRect()) + + (self.layer as? ContentScrollLayer)?.mirrorLayer = mirrorView.layer + self.canCancelContentTouches = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } + private struct ItemLayout { struct ItemDescription { var isStatic: Bool @@ -1394,9 +1486,11 @@ public final class EntityKeyboardTopPanelComponent: Component { } } - private let scrollView: UIScrollView + private let mirrorContentScrollView: UIView + private let scrollView: ContentScrollView private var itemViews: [AnyHashable: ComponentHostView] = [:] private var highlightedIconBackgroundView: UIView + private var highlightedIconTintBackgroundView: UIView private var temporaryReorderingOrderIndex: (id: AnyHashable, index: Int)? @@ -1429,21 +1523,35 @@ public final class EntityKeyboardTopPanelComponent: Component { private var reorderGestureRecognizer: ReorderGestureRecognizer? + public let tintContentMask: UIView + private var component: EntityKeyboardTopPanelComponent? weak var state: EmptyComponentState? private var environment: EntityKeyboardTopContainerPanelEnvironment? override init(frame: CGRect) { - self.scrollView = UIScrollView() + self.mirrorContentScrollView = UIView() + self.scrollView = ContentScrollView(mirrorView: self.mirrorContentScrollView) self.highlightedIconBackgroundView = UIView() self.highlightedIconBackgroundView.isUserInteractionEnabled = false self.highlightedIconBackgroundView.clipsToBounds = true self.highlightedIconBackgroundView.isHidden = true + self.highlightedIconTintBackgroundView = UIView() + self.highlightedIconTintBackgroundView.isUserInteractionEnabled = false + self.highlightedIconTintBackgroundView.clipsToBounds = true + self.highlightedIconTintBackgroundView.isHidden = true + self.highlightedIconTintBackgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.1) + + self.tintContentMask = UIView() + self.mirrorContentScrollView.addSubview(self.highlightedIconTintBackgroundView) + self.tintContentMask.addSubview(self.mirrorContentScrollView) + super.init(frame: frame) self.scrollView.layer.anchorPoint = CGPoint() + self.mirrorContentScrollView.layer.anchorPoint = CGPoint() self.scrollView.delaysContentTouches = false self.scrollView.clipsToBounds = false if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { @@ -1849,11 +1957,21 @@ public final class EntityKeyboardTopPanelComponent: Component { }, containerSize: itemOuterFrame.size ) + let itemFrame = CGRect(origin: CGPoint(x: itemOuterFrame.minX + floor((itemOuterFrame.width - itemSize.width) / 2.0), y: itemOuterFrame.minY + floor((itemOuterFrame.height - itemSize.height) / 2.0)), size: itemSize) itemTransition.setFrame(view: itemView, frame: itemFrame) - itemTransition.setSublayerTransform(view: itemView, transform: CATransform3DMakeScale(scale, scale, 1.0)) itemTransition.setAlpha(view: itemView, alpha: self.visibilityFraction) + + if let itemView = itemView.componentView as? EntityKeyboardIconTopPanelComponent.View { + if itemView.tintMaskView.superview == nil { + self.mirrorContentScrollView.addSubview(itemView.tintMaskView) + } + + itemTransition.setFrame(view: itemView.tintMaskView, frame: itemFrame) + itemTransition.setSublayerTransform(view: itemView.tintMaskView, transform: CATransform3DMakeScale(scale, scale, 1.0)) + itemTransition.setAlpha(view: itemView.tintMaskView, alpha: self.visibilityFraction) + } } } var removedIds: [AnyHashable] = [] @@ -1861,6 +1979,9 @@ public final class EntityKeyboardTopPanelComponent: Component { if !validIds.contains(id) { removedIds.append(id) itemView.removeFromSuperview() + if let itemView = itemView.componentView as? EntityKeyboardIconTopPanelComponent.View { + itemView.tintMaskView.removeFromSuperview() + } } } for id in removedIds { @@ -2020,6 +2141,7 @@ public final class EntityKeyboardTopPanelComponent: Component { if let activeContentItemId = self.activeContentItemId, activeContentItemId == self.items[index].id { self.highlightedIconBackgroundView.frame = itemOuterFrame + self.highlightedIconTintBackgroundView.frame = itemOuterFrame } } } @@ -2080,6 +2202,7 @@ public final class EntityKeyboardTopPanelComponent: Component { var highlightTransition = transition if self.highlightedIconBackgroundView.isHidden { self.highlightedIconBackgroundView.isHidden = false + self.highlightedIconTintBackgroundView.isHidden = false highlightTransition = .immediate } @@ -2089,16 +2212,24 @@ public final class EntityKeyboardTopPanelComponent: Component { } else { isRound = false } + highlightTransition.setCornerRadius(layer: self.highlightedIconBackgroundView.layer, cornerRadius: isRound ? min(itemFrame.width / 2.0, itemFrame.height / 2.0) : 10.0) highlightTransition.setPosition(view: self.highlightedIconBackgroundView, position: CGPoint(x: itemFrame.midX, y: itemFrame.midY)) highlightTransition.setBounds(view: self.highlightedIconBackgroundView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) + + highlightTransition.setCornerRadius(layer: self.highlightedIconTintBackgroundView.layer, cornerRadius: isRound ? min(itemFrame.width / 2.0, itemFrame.height / 2.0) : 10.0) + highlightTransition.setPosition(view: self.highlightedIconTintBackgroundView, position: CGPoint(x: itemFrame.midX, y: itemFrame.midY)) + highlightTransition.setBounds(view: self.highlightedIconTintBackgroundView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) } else { self.highlightedIconBackgroundView.isHidden = true + self.highlightedIconTintBackgroundView.isHidden = true } } else { self.highlightedIconBackgroundView.isHidden = true + self.highlightedIconTintBackgroundView.isHidden = true } transition.setAlpha(view: self.highlightedIconBackgroundView, alpha: isExpanded ? 0.0 : 1.0) + transition.setAlpha(view: self.highlightedIconTintBackgroundView, alpha: isExpanded ? 0.0 : 1.0) panelEnvironment.visibilityFractionUpdated.connect { [weak self] (fraction, transition) in guard let strongSelf = self else { @@ -2130,6 +2261,9 @@ public final class EntityKeyboardTopPanelComponent: Component { transition.setScale(view: self.highlightedIconBackgroundView, scale: scale) transition.setAlpha(view: self.highlightedIconBackgroundView, alpha: self.visibilityFraction) + transition.setScale(view: self.highlightedIconTintBackgroundView, scale: scale) + transition.setAlpha(view: self.highlightedIconTintBackgroundView, alpha: self.visibilityFraction) + for (_, itemView) in self.itemViews { transition.setSublayerTransform(view: itemView, transform: CATransform3DMakeScale(scale, scale, 1.0)) transition.setAlpha(view: itemView, alpha: self.visibilityFraction) @@ -2165,29 +2299,6 @@ public final class EntityKeyboardTopPanelComponent: Component { } } } - - /*var found = false - for i in 0 ..< self.items.count { - if self.items[i].id == itemId { - found = true - self.highlightedIconBackgroundView.isHidden = false - let itemFrame = itemLayout.containerFrame(at: i) - - var highlightTransition = transition - if highlightTransition.animation.isImmediate { - highlightTransition = highlightTransition.withAnimation(.curve(duration: 0.3, curve: .spring)) - } - highlightTransition.setPosition(view: self.highlightedIconBackgroundView, position: CGPoint(x: itemFrame.midX, y: itemFrame.midY)) - highlightTransition.setBounds(view: self.highlightedIconBackgroundView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) - - self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -6.0, dy: 0.0), animated: true) - - break - } - } - if !found { - self.highlightedIconBackgroundView.isHidden = true - }*/ } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 734f6f916a..1410cd9da9 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -696,20 +696,25 @@ public extension GlassBackgroundView { fillColor.getHue(nil, saturation: &s, brightness: &b, alpha: &a) let innerImage: UIImage - if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0) { + if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0), b >= 0.2 { innerImage = UIGraphicsImageRenderer(size: size).image { ctx in let context = ctx.cgContext context.setFillColor(fillColor.cgColor) - context.fill(CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset).insetBy(dx: 0.1, dy: 0.1)) + context.fill(CGRect(origin: CGPoint(), size: size)) if let image = UIImage(bundleImageName: "Item List/GlassEdge40x40") { - if s <= 0.3 && !isDark { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.7) + let imageInset = (image.size.width - 40.0) * 0.5 + + if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark { + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset - imageInset, dy: inset - imageInset), blendMode: .normal, alpha: 1.0) + } else if s <= 0.3 && !isDark { + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset - imageInset, dy: inset - imageInset), blendMode: .normal, alpha: 0.7) } else if b >= 0.2 { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .plusLighter, alpha: 0.7) + let maxAlpha: CGFloat = isDark ? 0.7 : 0.8 + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset - imageInset, dy: inset - imageInset), blendMode: .overlay, alpha: max(0.5, min(1.0, maxAlpha * s))) } else { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.5) + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset - imageInset, dy: inset - imageInset), blendMode: .normal, alpha: 0.5) } } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index bc8df56128..e5eed8438c 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -407,7 +407,7 @@ final class MediaEditorScreenComponent: Component { areCustomEmojiEnabled: true, hasSearch: true, hideBackground: true, - maskEdge: true, + maskEdge: .fade, sendGif: nil ) |> map { inputData -> ChatEntityKeyboardInputNode.InputData in return ChatEntityKeyboardInputNode.InputData( diff --git a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@2x.png b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@2x.png new file mode 100644 index 0000000000..a192b6a578 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@2x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@3x.png b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@3x.png new file mode 100644 index 0000000000..047efb09c9 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/18Glass@3x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/Contents.json index 2e0d6e19f6..5dc20ec7a1 100644 --- a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/Contents.json @@ -1,8 +1,18 @@ { "images" : [ { - "filename" : "iOS 28.pdf", - "idiom" : "universal" + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "18Glass@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "18Glass@3x.png", + "idiom" : "universal", + "scale" : "3x" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/iOS 28.pdf b/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/iOS 28.pdf deleted file mode 100644 index b6d25b544a..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Item List/GlassEdge40x40.imageset/iOS 28.pdf and /dev/null differ diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1648950bdb..4a48ba8fd2 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -10190,6 +10190,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var clearInputState = false if transferInputState { + self.chatDisplayNode.textInputPanelNode?.ignoreInputStateUpdates = true + var peerId: PeerId var currentThreadId: Int64? switch self.chatLocation { @@ -10294,6 +10296,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.currentChatSwitchDirection = nil self.isUpdatingChatLocationThread = false + self.chatDisplayNode.textInputPanelNode?.ignoreInputStateUpdates = false + if clearInputState { //DispatchQueue.main.async { [weak self] in // guard let self else { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index bcf11e3564..1419fdcd8d 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3856,6 +3856,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { areCustomEmojiEnabled: self.chatPresentationInterfaceState.customEmojiAvailable, hasEdit: true, hideBackground: true, + maskEdge: .clip, sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in if let self { return self.controllerInteraction.sendGif(fileReference, sourceView, sourceRect, silentPosting, schedule) @@ -5172,7 +5173,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } func prepareSwitchToChatLocation(chatLocation: ChatLocation, historyNode: ChatHistoryListNodeImpl, animationDirection: ChatControllerAnimateInnerChatSwitchDirection?) { - self.chatLocation = historyNode.chatLocation + self.chatLocation = chatLocation if historyNode === self.historyNode { historyNode.updateChatLocation(chatLocation: chatLocation) } else { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 88c4bb2aad..b2b08bdccb 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1092,6 +1092,17 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } } + if matches { + for (message, _) in item.content { + if strongSelf.chatLocation.threadId != nil { + if message.id.namespace != Namespaces.Message.Cloud { + matches = false + break + } + } + } + } + if matches { var maxItemIndex: MessageIndex? for (message, _) in item.content { @@ -1236,6 +1247,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } self.chatLocation = chatLocation + self.interactiveReadActionDisposable?.dispose() + self.interactiveReadActionDisposable = nil + self.beginChatHistoryTransitions(resetScrolling: true, switchedToAnotherSource: false) self.beginReadHistoryManagement() }