diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 4580f29dc2..93ac3ed360 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7886,6 +7886,10 @@ Sorry for the inconvenience."; "ChatContextMenu.EmojiSet_1" = "This message contains emoji from [%@ pack]()."; "ChatContextMenu.EmojiSet_any" = "This message contains emoji from [%@ packs]()."; +"ChatContextMenu.ReactionEmojiSetSingle" = "This message contains\n#[%@]() reactions."; +"ChatContextMenu.ReactionEmojiSet_1" = "This message contains reactions from [%@ pack]()."; +"ChatContextMenu.ReactionEmojiSet_any" = "This message contains reactions from [%@ packs]()."; + "EmojiPack.Title" = "Emoji"; "EmojiPack.Emoji_1" = "%@ emoji"; "EmojiPack.Emoji_any" = "%@ emoji"; diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index f89cafecc5..a4ef901b9f 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -673,7 +673,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { public var activateAfterCompletion: Bool = false { didSet { if self.activateAfterCompletion { - self.contextGesture?.activatedAfterCompletion = { [weak self] point in + self.contextGesture?.activatedAfterCompletion = { [weak self] point, _ in guard let strongSelf = self else { return } @@ -717,7 +717,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { } if self.activateAfterCompletion { - self.contextGesture?.activatedAfterCompletion = { [weak self] point in + self.contextGesture?.activatedAfterCompletion = { [weak self] point, _ in guard let strongSelf = self else { return } diff --git a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift index 6f90dd4983..e4c1dc66ed 100644 --- a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift +++ b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift @@ -473,6 +473,33 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { }) } + func animateTransitionInside(other: InnerTextSelectionTipContainerNode) { + let nodes: [ASDisplayNode] = [ + self.textNode.textNode, + self.iconNode, + self.placeholderNode + ] + + for node in nodes { + other.addSubnode(node) + node.layer.animateAlpha(from: node.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak node] _ in + node?.removeFromSupernode() + }) + } + } + + func animateContentIn() { + let nodes: [ASDisplayNode] = [ + self.textNode.textNode, + self.iconNode, + self.placeholderNode + ] + + for node in nodes { + node.layer.animateAlpha(from: 0.0, to: node.alpha, duration: 0.2) + } + } + func updateLayout(widthClass: ContainerViewLayoutSizeClass, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { switch widthClass { case .compact: diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 0f6deabd74..dbea7f3cad 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -250,6 +250,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi private let blurBackground: Bool + var overlayWantsToBeBelowKeyboard: Bool { + if let presentationNode = self.presentationNode { + return presentationNode.wantsDisplayBelowKeyboard() + } else { + return false + } + } + init( account: Account, controller: ContextController, @@ -624,6 +632,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi guard let strongSelf = self else { return } + if let validLayout = strongSelf.validLayout { strongSelf.updateLayout( layout: validLayout, @@ -632,6 +641,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi ) } }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let strongSelf = self else { + return + } + if let controller = strongSelf.getController() { + controller.overlayWantsToBeBelowKeyboardUpdated(transition: transition) + } + }, requestDismiss: { [weak self] result in guard let strongSelf = self else { return @@ -681,6 +698,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi ) } }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let strongSelf = self else { + return + } + if let controller = strongSelf.getController() { + controller.overlayWantsToBeBelowKeyboardUpdated(transition: transition) + } + }, requestDismiss: { [weak self] result in guard let strongSelf = self else { return @@ -716,6 +741,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi ) } }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let strongSelf = self else { + return + } + if let controller = strongSelf.getController() { + controller.overlayWantsToBeBelowKeyboardUpdated(transition: transition) + } + }, requestDismiss: { [weak self] result in guard let strongSelf = self else { return @@ -1525,25 +1558,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi reactionContextNode?.removeFromSupernode() }) } - - if !items.reactionItems.isEmpty, let context = items.context, let animationCache = items.animationCache { - let reactionContextNode = ReactionContextNode(context: context, animationCache: animationCache, presentationData: self.presentationData, items: items.reactionItems, selectedItems: items.selectedReactionItems, getEmojiContent: items.getEmojiContent, isExpandedUpdated: { _ in }, requestLayout: { _ in }) - self.reactionContextNode = reactionContextNode - self.addSubnode(reactionContextNode) - - reactionContextNode.reactionSelected = { [weak self] reaction, isLarge in - guard let strongSelf = self, let controller = strongSelf.getController() as? ContextController else { - return - } - controller.reactionSelected?(reaction, isLarge) - } - reactionContextNode.premiumReactionsSelected = { [weak self] _ in - guard let strongSelf = self, let controller = strongSelf.getController() as? ContextController else { - return - } - controller.premiumReactionsSelected?() - } - } let previousActionsContainerNode = self.actionsContainerNode let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view) @@ -2508,6 +2522,14 @@ public final class ContextController: ViewController, StandalonePresentableContr private var animatedDidAppear = false private var wasDismissed = false + override public var overlayWantsToBeBelowKeyboard: Bool { + if self.isNodeLoaded { + return self.controllerNode.overlayWantsToBeBelowKeyboard + } else { + return false + } + } + private var controllerNode: ContextControllerNode { return self.displayNode as! ContextControllerNode } diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 706342a77e..e2d6de9998 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -968,16 +968,20 @@ final class ContextControllerActionsStackNode: ASDisplayNode { var updatedTransition = transition if let tipNode = self.tipNode, tipNode.tip == tip { } else { - if let tipNode = self.tipNode { - self.tipNode = nil - tipNode.removeFromSupernode() - } + let previousTipNode = self.tipNode updatedTransition = .immediate let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip) tipNode.requestDismiss = { [weak self] completion in self?.getController()?.dismiss(completion: completion) } self.tipNode = tipNode + + if let previousTipNode = previousTipNode { + previousTipNode.animateTransitionInside(other: tipNode) + previousTipNode.removeFromSupernode() + + tipNode.animateContentIn() + } } if let tipNode = self.tipNode { diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index f2d253615d..ef13561317 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -172,6 +172,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo private let getController: () -> ContextControllerProtocol? private let requestUpdate: (ContainedViewLayoutTransition) -> Void + private let requestUpdateOverlayWantsToBeBelowKeyboard: (ContainedViewLayoutTransition) -> Void private let requestDismiss: (ContextMenuActionResult) -> Void private let requestAnimateOut: (ContextMenuActionResult, @escaping () -> Void) -> Void private let source: ContentSource @@ -208,12 +209,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo init( getController: @escaping () -> ContextControllerProtocol?, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, + requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void, requestDismiss: @escaping (ContextMenuActionResult) -> Void, requestAnimateOut: @escaping (ContextMenuActionResult, @escaping () -> Void) -> Void, source: ContentSource ) { self.getController = getController self.requestUpdate = requestUpdate + self.requestUpdateOverlayWantsToBeBelowKeyboard = requestUpdateOverlayWantsToBeBelowKeyboard self.requestDismiss = requestDismiss self.requestAnimateOut = requestAnimateOut self.source = source @@ -394,6 +397,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.actionsStackNode.increaseHighlightedIndex() } + func wantsDisplayBelowKeyboard() -> Bool { + if let reactionContextNode = self.reactionContextNode { + return reactionContextNode.wantsDisplayBelowKeyboard() + } else { + return false + } + } + func replaceItems(items: ContextController.Items, animated: Bool) { self.actionsStackNode.replace(item: makeContextControllerActionsStackItem(items: items), animated: animated) } @@ -532,6 +543,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo return } strongSelf.requestUpdate(transition) + }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let strongSelf = self else { + return + } + strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition) } ) self.reactionContextNode = reactionContextNode diff --git a/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift index d9a4a625c6..1fb23869ff 100644 --- a/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift @@ -17,6 +17,7 @@ protocol ContextControllerPresentationNode: ASDisplayNode { func replaceItems(items: ContextController.Items, animated: Bool) func pushItems(items: ContextController.Items) func popItems() + func wantsDisplayBelowKeyboard() -> Bool func update( presentationData: PresentationData, diff --git a/submodules/Display/Source/ContextGesture.swift b/submodules/Display/Source/ContextGesture.swift index a3aa7b917a..0c1700570d 100644 --- a/submodules/Display/Source/ContextGesture.swift +++ b/submodules/Display/Source/ContextGesture.swift @@ -57,6 +57,7 @@ private func cancelOtherGestures(gesture: ContextGesture, view: UIView) { public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDelegate { public var beginDelay: Double = 0.12 + public var activateOnTap: Bool = false private var currentProgress: CGFloat = 0.0 private var delayTimer: Timer? private var animator: DisplayLinkAnimator? @@ -68,7 +69,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg public var activated: ((ContextGesture, CGPoint) -> Void)? public var externalUpdated: ((UIView?, CGPoint) -> Void)? public var externalEnded: (((UIView?, CGPoint)?) -> Void)? - public var activatedAfterCompletion: ((CGPoint) -> Void)? + public var activatedAfterCompletion: ((CGPoint, Bool) -> Void)? public var cancelGesturesOnActivation: (() -> Void)? override public init(target: Any?, action: Selector?) { @@ -208,7 +209,12 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg self.currentProgress = 0.0 self.activationProgress?(0.0, .ended(self.currentProgress)) if self.wasActivated { - self.activatedAfterCompletion?(touch.location(in: self.view)) + self.activatedAfterCompletion?(touch.location(in: self.view), false) + } + } else { + self.currentProgress = 0.0 + if !self.wasActivated && self.activateOnTap { + self.activatedAfterCompletion?(touch.location(in: self.view), true) } } diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 734433942b..16b0ba0d09 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -154,6 +154,7 @@ open class NavigationController: UINavigationController, ContainableController, private var overlayContainers: [NavigationOverlayContainer] = [] private var globalOverlayContainers: [NavigationOverlayContainer] = [] + private var globalOverlayBelowKeyboardContainerParent: GlobalOverlayContainerParent? private var globalOverlayContainerParent: GlobalOverlayContainerParent? public var globalOverlayControllersUpdated: (() -> Void)? @@ -351,7 +352,7 @@ open class NavigationController: UINavigationController, ContainableController, private var isUpdatingContainers: Bool = false - private func updateContainersNonReentrant(transition: ContainedViewLayoutTransition) { + func updateContainersNonReentrant(transition: ContainedViewLayoutTransition) { if self.isUpdatingContainers { return } @@ -375,7 +376,18 @@ open class NavigationController: UINavigationController, ContainableController, let initialPrefersOnScreenNavigationHidden = self.collectPrefersOnScreenNavigationHidden() - var overlayLayout = layout + let belowKeyboardOverlayLayout = layout + var globalOverlayLayout = layout + + if let globalOverlayBelowKeyboardContainerParent = self.globalOverlayBelowKeyboardContainerParent { + if globalOverlayBelowKeyboardContainerParent.view.superview != self.displayNode.view { + self.displayNode.addSubnode(globalOverlayBelowKeyboardContainerParent) + } + + /*overlayLayout.size.height = overlayLayout.size.height - (layout.inputHeight ?? 0.0) + overlayLayout.inputHeight = nil + overlayLayout.inputHeightIsInteractivellyChanging = false*/ + } if let globalOverlayContainerParent = self.globalOverlayContainerParent { let portraitSize = CGSize(width: min(layout.size.width, layout.size.height), height: max(layout.size.width, layout.size.height)) @@ -386,9 +398,9 @@ open class NavigationController: UINavigationController, ContainableController, self.displayNode.addSubnode(globalOverlayContainerParent) } - overlayLayout.size.height = overlayLayout.size.height - (layout.inputHeight ?? 0.0) - overlayLayout.inputHeight = nil - overlayLayout.inputHeightIsInteractivellyChanging = false + globalOverlayLayout.size.height = globalOverlayLayout.size.height - (layout.inputHeight ?? 0.0) + globalOverlayLayout.inputHeight = nil + globalOverlayLayout.inputHeightIsInteractivellyChanging = false } else if layout.inputHeight == nil { if globalOverlayContainerParent.view.superview != self.displayNode.view { self.displayNode.addSubnode(globalOverlayContainerParent) @@ -438,6 +450,9 @@ open class NavigationController: UINavigationController, ContainableController, } } + if let globalOverlayBelowKeyboardContainerParent = self.globalOverlayBelowKeyboardContainerParent { + transition.updateFrame(node: globalOverlayBelowKeyboardContainerParent, frame: CGRect(origin: CGPoint(), size: layout.size)) + } if let globalOverlayContainerParent = self.globalOverlayContainerParent { transition.updateFrame(node: globalOverlayContainerParent, frame: CGRect(origin: CGPoint(), size: layout.size)) } @@ -525,6 +540,7 @@ open class NavigationController: UINavigationController, ContainableController, var additionalSideInsets = UIEdgeInsets() var modalStyleOverlayTransitionFactor: CGFloat = 0.0 + var previousGlobalOverlayBelowKeyboardContainer: NavigationOverlayContainer? var previousGlobalOverlayContainer: NavigationOverlayContainer? for i in (0 ..< self.globalOverlayContainers.count).reversed() { let overlayContainer = self.globalOverlayContainers[i] @@ -536,26 +552,61 @@ open class NavigationController: UINavigationController, ContainableController, containerTransition = transition } + let overlayWantsToBeBelowKeyboard = overlayContainer.controller.overlayWantsToBeBelowKeyboard + let overlayLayout: ContainerViewLayout + if overlayWantsToBeBelowKeyboard { + overlayLayout = belowKeyboardOverlayLayout + } else { + overlayLayout = globalOverlayLayout + } + containerTransition.updateFrame(node: overlayContainer, frame: CGRect(origin: CGPoint(), size: overlayLayout.size)) overlayContainer.update(layout: overlayLayout, transition: containerTransition) modalStyleOverlayTransitionFactor = max(modalStyleOverlayTransitionFactor, overlayContainer.controller.modalStyleOverlayTransitionFactor) - if overlayContainer.supernode == nil && overlayContainer.isReady { - if let previousGlobalOverlayContainer = previousGlobalOverlayContainer { - self.globalOverlayContainerParent?.insertSubnode(overlayContainer, belowSubnode: previousGlobalOverlayContainer) + if overlayContainer.isReady { + let wasNotAdded = overlayContainer.supernode == nil + + if overlayWantsToBeBelowKeyboard { + if overlayContainer.supernode !== self.globalOverlayBelowKeyboardContainerParent { + if let previousGlobalOverlayBelowKeyboardContainer = previousGlobalOverlayBelowKeyboardContainer { + self.globalOverlayBelowKeyboardContainerParent?.insertSubnode(overlayContainer, belowSubnode: previousGlobalOverlayBelowKeyboardContainer) + } else { + self.globalOverlayBelowKeyboardContainerParent?.addSubnode(overlayContainer) + } + } } else { - self.globalOverlayContainerParent?.addSubnode(overlayContainer) + if overlayContainer.supernode !== self.globalOverlayContainerParent { + if let previousGlobalOverlayContainer = previousGlobalOverlayContainer { + self.globalOverlayContainerParent?.insertSubnode(overlayContainer, belowSubnode: previousGlobalOverlayContainer) + } else { + self.globalOverlayContainerParent?.addSubnode(overlayContainer) + } + } + } + + if wasNotAdded { + overlayContainer.transitionIn() + notifyGlobalOverlayControllersUpdated = true + overlayContainer.controller.internalOverlayWantsToBeBelowKeyboardUpdated = { [weak self] transition in + guard let strongSelf = self else { + return + } + strongSelf.updateContainersNonReentrant(transition: transition) + } } - overlayContainer.transitionIn() - notifyGlobalOverlayControllersUpdated = true } let controllerAdditionalSideInsets = overlayContainer.controller.additionalSideInsets additionalSideInsets = UIEdgeInsets(top: 0.0, left: max(additionalSideInsets.left, controllerAdditionalSideInsets.left), bottom: 0.0, right: max(additionalSideInsets.right, controllerAdditionalSideInsets.right)) if overlayContainer.supernode != nil { - previousGlobalOverlayContainer = overlayContainer + if overlayContainer.controller.overlayWantsToBeBelowKeyboard { + previousGlobalOverlayBelowKeyboardContainer = overlayContainer + } else { + previousGlobalOverlayContainer = overlayContainer + } let controllerStatusBarStyle = overlayContainer.controller.statusBar.statusBarStyle switch controllerStatusBarStyle { case .Black, .White, .Hide: @@ -594,6 +645,8 @@ open class NavigationController: UINavigationController, ContainableController, self.displayNode.insertSubnode(overlayContainer, belowSubnode: previousOverlayContainer) } else if let globalScrollToTopNode = self.globalScrollToTopNode { self.displayNode.insertSubnode(overlayContainer, belowSubnode: globalScrollToTopNode) + } else if let globalOverlayBelowKeyboardContainerParent = self.globalOverlayBelowKeyboardContainerParent { + self.displayNode.insertSubnode(overlayContainer, belowSubnode: globalOverlayBelowKeyboardContainerParent) } else if let globalOverlayContainerParent = self.globalOverlayContainerParent { self.displayNode.insertSubnode(overlayContainer, belowSubnode: globalOverlayContainerParent) } else { @@ -674,7 +727,7 @@ open class NavigationController: UINavigationController, ContainableController, } containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) - modalContainer.update(layout: modalContainer.isFlat ? overlayLayout : layout, controllers: navigationLayout.modal[i].controllers, coveredByModalTransition: effectiveModalTransition, transition: containerTransition) + modalContainer.update(layout: modalContainer.isFlat ? globalOverlayLayout : layout, controllers: navigationLayout.modal[i].controllers, coveredByModalTransition: effectiveModalTransition, transition: containerTransition) if modalContainer.supernode == nil && modalContainer.isReady { if let previousModalContainer = previousModalContainer { @@ -1228,6 +1281,10 @@ open class NavigationController: UINavigationController, ContainableController, self.displayNode.addSubnode(globalScrollToTopNode) self.globalScrollToTopNode = globalScrollToTopNode + let globalOverlayBelowKeyboardContainerParent = GlobalOverlayContainerParent() + self.displayNode.addSubnode(globalOverlayBelowKeyboardContainerParent) + self.globalOverlayBelowKeyboardContainerParent = globalOverlayBelowKeyboardContainerParent + let globalOverlayContainerParent = GlobalOverlayContainerParent() self.displayNode.addSubnode(globalOverlayContainerParent) self.globalOverlayContainerParent = globalOverlayContainerParent diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index 8689ccc8e2..f699db47a4 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -210,6 +210,15 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open var hasActiveInput: Bool = false + open var overlayWantsToBeBelowKeyboard: Bool { + return false + } + + var internalOverlayWantsToBeBelowKeyboardUpdated: ((ContainedViewLayoutTransition) -> Void)? + public func overlayWantsToBeBelowKeyboardUpdated(transition: ContainedViewLayoutTransition) { + self.internalOverlayWantsToBeBelowKeyboardUpdated?(transition) + } + private var navigationBarOrigin: CGFloat = 0.0 open func navigationLayout(layout: ContainerViewLayout) -> NavigationLayout { diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 5b2beef721..cfde70badf 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -134,6 +134,36 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } + private final class ContentScrollView: UIScrollView { + override static var layerClass: AnyClass { + return EmojiPagerContentComponent.View.ContentScrollLayer.self + } + + init(mirrorView: UIView) { + super.init(frame: CGRect()) + + (self.layer as? EmojiPagerContentComponent.View.ContentScrollLayer)?.mirrorLayer = mirrorView.layer + } + + required init(coder: NSCoder) { + preconditionFailure() + } + } + + private final class ContentScrollNode: ASDisplayNode { + override var view: ContentScrollView { + return super.view as! ContentScrollView + } + + init(mirrorView: UIView) { + super.init() + + self.setViewBlock({ + return ContentScrollView(mirrorView: mirrorView) + }) + } + } + private let context: AccountContext private let presentationData: PresentationData private let animationCache: AnimationCache @@ -143,6 +173,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { private let getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? private let isExpandedUpdated: (ContainedViewLayoutTransition) -> Void private let requestLayout: (ContainedViewLayoutTransition) -> Void + private let requestUpdateOverlayWantsToBeBelowKeyboard: (ContainedViewLayoutTransition) -> Void private let backgroundNode: ReactionContextBackgroundNode @@ -152,7 +183,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { private let leftBackgroundMaskNode: ASDisplayNode private let rightBackgroundMaskNode: ASDisplayNode private let backgroundMaskNode: ASDisplayNode - private let scrollNode: ASScrollNode + private let mirrorContentScrollView: UIView + private let scrollNode: ContentScrollNode private let previewingItemContainer: ASDisplayNode private var visibleItemNodes: [Int: ReactionItemNode] = [:] private var disappearingVisibleItemNodes: [Int: ReactionItemNode] = [:] @@ -253,7 +285,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void) { + public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) { self.context = context self.presentationData = presentationData self.items = items @@ -261,6 +293,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.getEmojiContent = getEmojiContent self.isExpandedUpdated = isExpandedUpdated self.requestLayout = requestLayout + self.requestUpdateOverlayWantsToBeBelowKeyboard = requestUpdateOverlayWantsToBeBelowKeyboard self.animationCache = animationCache self.animationRenderer = MultiAnimationRendererImpl() @@ -274,7 +307,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.backgroundMaskNode.addSubnode(self.leftBackgroundMaskNode) self.backgroundMaskNode.addSubnode(self.rightBackgroundMaskNode) - self.scrollNode = ASScrollNode() + self.mirrorContentScrollView = UIView() + self.mirrorContentScrollView.isUserInteractionEnabled = false + + self.scrollNode = ContentScrollNode(mirrorView: self.mirrorContentScrollView) self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true self.scrollNode.view.showsVerticalScrollIndicator = false self.scrollNode.view.showsHorizontalScrollIndicator = false @@ -297,6 +333,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.contentTintContainer.clipsToBounds = true self.contentTintContainer.isUserInteractionEnabled = false + self.contentTintContainer.view.addSubview(self.mirrorContentScrollView) + self.contentContainerMask = UIImageView() self.contentContainerMask.image = generateImage(CGSize(width: 46.0, height: 46.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -491,6 +529,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } + public func wantsDisplayBelowKeyboard() -> Bool { + if let emojiView = self.reactionSelectionComponentHost?.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View { + return emojiView.wantsDisplayBelowKeyboard() + } else { + return false + } + } + private func calculateBackgroundFrame(containerSize: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, contentSize: CGSize) -> (backgroundFrame: CGRect, visualBackgroundFrame: CGRect, isLeftAligned: Bool, cloudSourcePoint: CGFloat) { var contentSize = contentSize contentSize.width = max(46.0, contentSize.width) @@ -690,7 +736,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.addSubnode(itemNode) if let itemNode = itemNode as? ReactionNode { if let reaction = self.items[i].reaction, self.selectedItems.contains(reaction.rawValue) { - self.contentTintContainer.view.addSubview(itemNode.selectionTintView) + self.mirrorContentScrollView.addSubview(itemNode.selectionTintView) self.scrollNode.view.addSubview(itemNode.selectionView) } } @@ -784,7 +830,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { expandItemSize = 30.0 expandTintOffset = 0.0 } - let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? 46.0 : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) + let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) transition.updateFrame(view: expandItemView, frame: baseNextFrame) transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame.offsetBy(dx: 0.0, dy: expandTintOffset)) @@ -885,7 +931,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { visibleItemCount: itemCount ) - var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? 46.0 : 0.0), size: actualBackgroundFrame.size) + var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0), size: actualBackgroundFrame.size) scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0) transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true) @@ -977,7 +1023,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { if let mirrorContentClippingView = emojiView.mirrorContentClippingView { mirrorContentClippingView.clipsToBounds = false - Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in + Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0 + 54.0 - 4.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in mirrorContentClippingView?.clipsToBounds = true }) } @@ -1007,7 +1053,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) if animateIn { - transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -46.0 + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) + transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0 + 54.0 - 4.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) } } } @@ -1195,6 +1241,12 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { navigationController: { return nil }, + requestUpdate: { [weak self] transition in + guard let strongSelf = self else { + return + } + strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition) + }, sendSticker: nil, chatPeerId: nil, peekBehavior: nil, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 8af73e37a4..1d24f2c939 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -898,7 +898,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio panelContentVibrantOverlayColor: UIColor(white: 0.7, alpha: 0.65), panelContentControlVibrantOverlayColor: UIColor(white: 0.85, alpha: 0.65), panelContentControlVibrantSelectionColor: UIColor(white: 0.85, alpha: 0.1), - panelContentControlOpaqueOverlayColor: UIColor(white: 0.0, alpha: 0.3), + panelContentControlOpaqueOverlayColor: UIColor(white: 0.0, alpha: 0.2), panelContentControlOpaqueSelectionColor: UIColor(white: 0.0, alpha: 0.1), stickersBackgroundColor: UIColor(rgb: 0xe8ebf0), stickersSectionTextColor: UIColor(rgb: 0x9099a2), diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index 036e779d43..0760f120df 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -341,8 +341,14 @@ public final class EmojiStatusComponent: Component { } } iconView.image = iconImage - size = iconImage.size.aspectFilled(availableSize) - iconView.frame = CGRect(origin: CGPoint(), size: size) + + if case .text = component.content { + size = CGSize(width: iconImage.size.width, height: availableSize.height) + iconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconImage.size.width) / 2.0), y: floor((size.height - iconImage.size.height) / 2.0)), size: iconImage.size) + } else { + size = iconImage.size.aspectFilled(availableSize) + iconView.frame = CGRect(origin: CGPoint(), size: size) + } } else { if let iconView = self.iconView { self.iconView = nil diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index baf97b3e5f..f7d1fcdaaf 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -146,6 +146,7 @@ public final class EmojiStatusSelectionComponent: Component { component: AnyComponent(EntityKeyboardComponent( theme: component.theme, strings: component.strings, + isContentInFocus: true, containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0), topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), emojiContent: component.emojiContent, @@ -340,6 +341,8 @@ public final class EmojiStatusSelectionController: ViewController { navigationController: { return nil }, + requestUpdate: { _ in + }, sendSticker: nil, chatPeerId: nil, peekBehavior: nil, diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index f2b9e32da2..5ec38ab6d5 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1049,6 +1049,10 @@ private final class GroupHeaderLayer: UIView { self.subtitleLayer = nil subtitleLayer.removeFromSuperlayer() } + if let tintSubtitleLayer = self.tintSubtitleLayer { + self.tintSubtitleLayer = nil + tintSubtitleLayer.removeFromSuperlayer() + } } var clearWidth: CGFloat = 0.0 @@ -1178,9 +1182,9 @@ private final class GroupHeaderLayer: UIView { return super.hitTest(point, with: event) } - func tapGesture(_ recognizer: UITapGestureRecognizer) -> Bool { + func tapGesture(point: CGPoint) -> Bool { if let groupEmbeddedView = self.groupEmbeddedView { - return groupEmbeddedView.tapGesture(recognizer) + return groupEmbeddedView.tapGesture(point: self.convert(point, to: groupEmbeddedView)) } else { return false } @@ -1261,18 +1265,15 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager fatalError("init(coder:) has not been implemented") } - func tapGesture(_ recognizer: UITapGestureRecognizer) -> Bool { + func tapGesture(point: CGPoint) -> Bool { guard let itemLayout = self.itemLayout else { return false } - if case .ended = recognizer.state { - let point = recognizer.location(in: self) - for (_, itemLayer) in self.visibleItemLayers { - if itemLayer.frame.inset(by: UIEdgeInsets(top: 6.0, left: itemLayout.itemSpacing, bottom: 6.0, right: itemLayout.itemSpacing)).contains(point) { - self.performItemAction(itemLayer.item, self, itemLayer.frame, itemLayer) - return true - } + for (_, itemLayer) in self.visibleItemLayers { + if itemLayer.frame.inset(by: UIEdgeInsets(top: 6.0, left: itemLayout.itemSpacing, bottom: 6.0, right: itemLayout.itemSpacing)).contains(point) { + self.performItemAction(itemLayer.item, self, itemLayer.frame, itemLayer) + return true } } @@ -1456,9 +1457,9 @@ private final class GroupExpandActionButton: UIButton { let color = theme.list.itemCheckColors.foregroundColor if useOpaqueTheme { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor - } else { self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor.cgColor + } else { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor } self.tintContainerLayer.backgroundColor = UIColor.white.cgColor @@ -1499,6 +1500,96 @@ private final class GroupExpandActionButton: UIButton { } } +private final class SearchHeaderView: UIView, UITextFieldDelegate { + override static var layerClass: AnyClass { + return PassthroughLayer.self + } + + private let requestUpdate: () -> Void + + let tintContainerLayer: SimpleLayer + + private let backgroundLayer: SimpleLayer + private let tintBackgroundLayer: SimpleLayer + + private var tapRecognizer: UITapGestureRecognizer? + private var textField: UITextField? + + var wantsDisplayBelowKeyboard: Bool { + return self.textField != nil + } + + init(requestUpdate: @escaping () -> Void) { + self.requestUpdate = requestUpdate + + self.tintContainerLayer = SimpleLayer() + + self.backgroundLayer = SimpleLayer() + self.tintBackgroundLayer = SimpleLayer() + + super.init(frame: CGRect()) + + self.layer.addSublayer(self.backgroundLayer) + self.tintContainerLayer.addSublayer(self.tintBackgroundLayer) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerLayer + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.addGestureRecognizer(tapRecognizer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + /*if self.textField == nil { + let textField = UITextField(frame: self.backgroundLayer.frame.insetBy(dx: 10.0, dy: 0.0)) + self.textField = textField + self.addSubview(textField) + textField.delegate = self + + self.requestUpdate() + textField.becomeFirstResponder() + }*/ + } + } + + func textFieldDidBeginEditing(_ textField: UITextField) { + } + + func textFieldDidEndEditing(_ textField: UITextField) { + } + + func update(theme: PresentationTheme, useOpaqueTheme: Bool, title: String, size: CGSize, transition: Transition) { + let sideInset: CGFloat = 8.0 + let topInset: CGFloat = 8.0 + let inputHeight: CGFloat = 36.0 + + if useOpaqueTheme { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor + self.tintBackgroundLayer.backgroundColor = UIColor.white.cgColor + } else { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor + self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor + } + + self.backgroundLayer.cornerRadius = inputHeight * 0.5 + self.tintBackgroundLayer.cornerRadius = inputHeight * 0.5 + + let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight)) + transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame) + transition.setFrame(layer: self.tintBackgroundLayer, frame: backgroundFrame) + + if let textField = self.textField { + textField.textColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor + textField.frame = backgroundFrame + } + } +} + public protocol EmojiContentPeekBehavior: AnyObject { func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?) } @@ -1576,6 +1667,7 @@ public final class EmojiPagerContentComponent: Component { public let presentController: (ViewController) -> Void public let presentGlobalOverlayController: (ViewController) -> Void public let navigationController: () -> NavigationController? + public let requestUpdate: (Transition) -> Void public let sendSticker: ((FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Void)? public let chatPeerId: PeerId? public let peekBehavior: EmojiContentPeekBehavior? @@ -1595,6 +1687,7 @@ public final class EmojiPagerContentComponent: Component { presentController: @escaping (ViewController) -> Void, presentGlobalOverlayController: @escaping (ViewController) -> Void, navigationController: @escaping () -> NavigationController?, + requestUpdate: @escaping (Transition) -> Void, sendSticker: ((FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Void)?, chatPeerId: PeerId?, peekBehavior: EmojiContentPeekBehavior?, @@ -1613,6 +1706,7 @@ public final class EmojiPagerContentComponent: Component { self.presentController = presentController self.presentGlobalOverlayController = presentGlobalOverlayController self.navigationController = navigationController + self.requestUpdate = requestUpdate self.sendSticker = sendSticker self.chatPeerId = chatPeerId self.peekBehavior = peekBehavior @@ -1824,6 +1918,7 @@ public final class EmojiPagerContentComponent: Component { public let itemGroups: [ItemGroup] public let itemLayoutType: ItemLayoutType public let warpContentsOnEdges: Bool + public let displaySearch: Bool public let enableLongPress: Bool public let selectedItems: Set @@ -1837,6 +1932,7 @@ public final class EmojiPagerContentComponent: Component { itemGroups: [ItemGroup], itemLayoutType: ItemLayoutType, warpContentsOnEdges: Bool, + displaySearch: Bool, enableLongPress: Bool, selectedItems: Set ) { @@ -1849,6 +1945,7 @@ public final class EmojiPagerContentComponent: Component { self.itemGroups = itemGroups self.itemLayoutType = itemLayoutType self.warpContentsOnEdges = warpContentsOnEdges + self.displaySearch = displaySearch self.enableLongPress = enableLongPress self.selectedItems = selectedItems } @@ -1864,6 +1961,7 @@ public final class EmojiPagerContentComponent: Component { itemGroups: itemGroups, itemLayoutType: self.itemLayoutType, warpContentsOnEdges: self.warpContentsOnEdges, + displaySearch: self.displaySearch, enableLongPress: self.enableLongPress, selectedItems: self.selectedItems ) @@ -1900,6 +1998,9 @@ public final class EmojiPagerContentComponent: Component { if lhs.warpContentsOnEdges != rhs.warpContentsOnEdges { return false } + if lhs.displaySearch != rhs.displaySearch { + return false + } if lhs.enableLongPress != rhs.enableLongPress { return false } @@ -1960,16 +2061,22 @@ public final class EmojiPagerContentComponent: Component { var itemsPerRow: Int var contentSize: CGSize + var searchInsets: UIEdgeInsets + var searchHeight: CGFloat + var premiumButtonInset: CGFloat var premiumButtonHeight: CGFloat - init(layoutType: ItemLayoutType, width: CGFloat, containerInsets: UIEdgeInsets, itemGroups: [ItemGroupDescription], expandedGroupIds: Set, curveNearBounds: Bool, customLayout: CustomLayout?) { + init(layoutType: ItemLayoutType, width: CGFloat, containerInsets: UIEdgeInsets, itemGroups: [ItemGroupDescription], expandedGroupIds: Set, curveNearBounds: Bool, displaySearch: Bool, customLayout: CustomLayout?) { self.layoutType = layoutType self.width = width self.premiumButtonInset = 6.0 self.premiumButtonHeight = 50.0 + self.searchHeight = 54.0 + self.searchInsets = UIEdgeInsets(top: max(0.0, containerInsets.top - 8.0), left: containerInsets.left, bottom: 0.0, right: containerInsets.right) + self.curveNearBounds = curveNearBounds let minItemsPerRow: Int @@ -2029,6 +2136,10 @@ public final class EmojiPagerContentComponent: Component { self.itemInsets.left = floorToScreenPixels((width - actualContentWidth) / 2.0) self.itemInsets.right = self.itemInsets.left + if displaySearch { + self.itemInsets.top += self.searchHeight - 4.0 + } + var verticalGroupOrigin: CGFloat = self.itemInsets.top self.itemGroupLayouts = [] for itemGroup in itemGroups { @@ -2550,22 +2661,22 @@ public final class EmojiPagerContentComponent: Component { } } - private final class ContentScrollLayer: CALayer { - var mirrorLayer: CALayer? + public final class ContentScrollLayer: CALayer { + public var mirrorLayer: CALayer? - override init() { + override public init() { super.init() } - override init(layer: Any) { + override public init(layer: Any) { super.init(layer: layer) } - required init?(coder: NSCoder) { + required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override var position: CGPoint { + override public var position: CGPoint { get { return super.position } set(value) { @@ -2576,7 +2687,7 @@ public final class EmojiPagerContentComponent: Component { } } - override var bounds: CGRect { + override public var bounds: CGRect { get { return super.bounds } set(value) { @@ -2587,7 +2698,7 @@ public final class EmojiPagerContentComponent: Component { } } - override func add(_ animation: CAAnimation, forKey key: String?) { + override public func add(_ animation: CAAnimation, forKey key: String?) { if let mirrorLayer = self.mirrorLayer { mirrorLayer.add(animation, forKey: key) } @@ -2595,7 +2706,7 @@ public final class EmojiPagerContentComponent: Component { super.add(animation, forKey: key) } - override func removeAllAnimations() { + override public func removeAllAnimations() { if let mirrorLayer = self.mirrorLayer { mirrorLayer.removeAllAnimations() } @@ -2603,7 +2714,7 @@ public final class EmojiPagerContentComponent: Component { super.removeAllAnimations() } - override func removeAnimation(forKey: String) { + override public func removeAnimation(forKey: String) { if let mirrorLayer = self.mirrorLayer { mirrorLayer.removeAnimation(forKey: forKey) } @@ -2625,11 +2736,16 @@ public final class EmojiPagerContentComponent: Component { 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 enum VisualItemKey: Hashable { @@ -2654,6 +2770,7 @@ public final class EmojiPagerContentComponent: Component { private var effectiveVisibleSize: CGSize = CGSize() private let placeholdersContainerView: UIView + private var visibleSearchHeader: SearchHeaderView? private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:] private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:] private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:] @@ -2672,6 +2789,8 @@ public final class EmojiPagerContentComponent: Component { private var activeItemUpdated: ActionSlot<(AnyHashable, AnyHashable?, Transition)>? private var itemLayout: ItemLayout? + private var contextFocusItemKey: EmojiPagerContentComponent.View.ItemLayer.Key? + private var longTapRecognizer: UILongPressGestureRecognizer? override init(frame: CGRect) { @@ -2724,7 +2843,128 @@ public final class EmojiPagerContentComponent: Component { self.scrollView.addSubview(self.placeholdersContainerView) - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + let contextGesture = ContextGesture(target: self, action: #selector(self.tapGesture(_:))) + contextGesture.activateOnTap = true + contextGesture.shouldBegin = { [weak self] point in + guard let `self` = self, let _ = self.component else { + return false + } + + let locationInScrollView = self.convert(point, to: self.scrollView) + outer: for (_, groupHeader) in self.visibleGroupHeaders { + if groupHeader.frame.insetBy(dx: -10.0, dy: -6.0).contains(locationInScrollView) { + let groupHeaderPoint = self.scrollView.convert(locationInScrollView, to: groupHeader) + if let clearIconLayer = groupHeader.clearIconLayer, clearIconLayer.frame.insetBy(dx: -4.0, dy: -4.0).contains(groupHeaderPoint) { + return true + } else { + return true + } + } + } + + var foundItem = false + var foundExactItem = false + if let (_, itemKey) = self.item(atPoint: point), let itemLayer = self.visibleItemLayers[itemKey] { + foundExactItem = true + foundItem = true + if !itemLayer.displayPlaceholder { + self.contextFocusItemKey = itemKey + return true + } + } + + if !foundExactItem { + if let (_, itemKey) = self.item(atPoint: point, extendedHitRange: true), let itemLayer = self.visibleItemLayers[itemKey] { + foundItem = true + if !itemLayer.displayPlaceholder { + self.contextFocusItemKey = itemKey + return true + } + } + } + + let _ = foundItem + + return false + } + contextGesture.activationProgress = { [weak self] progress, transition in + guard let self = self, let contextFocusItemKey = self.contextFocusItemKey else { + return + } + if let itemLayer = self.visibleItemLayers[contextFocusItemKey] { + switch transition { + case .begin: + break + case .update: + ContainedViewLayoutTransition.immediate.updateTransformScale(layer: itemLayer, scale: 1.0 * (1.0 - progress) + 0.7 * progress) + case let .ended(previousValue): + let _ = previousValue + } + } + } + contextGesture.activatedAfterCompletion = { [weak self] point, wasTap in + guard let `self` = self, let component = self.component else { + return + } + + if let contextFocusItemKey = self.contextFocusItemKey { + self.contextFocusItemKey = nil + if let itemLayer = self.visibleItemLayers[contextFocusItemKey] { + if wasTap { + let transition: ContainedViewLayoutTransition = .animated(duration: 0.08, curve: .linear) + transition.updateTransformScale(layer: itemLayer, scale: 0.7, completion: { [weak itemLayer] _ in + guard let itemLayer = itemLayer else { + return + } + let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .spring) + transition.updateTransformScale(layer: itemLayer, scale: 1.0) + }) + } else { + let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .spring) + transition.updateTransformScale(layer: itemLayer, scale: 1.0) + } + } + } + + let locationInScrollView = self.convert(point, to: self.scrollView) + outer: for (id, groupHeader) in self.visibleGroupHeaders { + if groupHeader.frame.insetBy(dx: -10.0, dy: -6.0).contains(locationInScrollView) { + let groupHeaderPoint = self.scrollView.convert(locationInScrollView, to: groupHeader) + if let clearIconLayer = groupHeader.clearIconLayer, clearIconLayer.frame.insetBy(dx: -4.0, dy: -4.0).contains(groupHeaderPoint) { + component.inputInteractionHolder.inputInteraction?.clearGroup(id) + return + } else { + if groupHeader.tapGesture(point: self.convert(point, to: groupHeader)) { + return + } + } + } + } + + var foundItem = false + var foundExactItem = false + if let (item, itemKey) = self.item(atPoint: point), let itemLayer = self.visibleItemLayers[itemKey] { + foundExactItem = true + foundItem = true + if !itemLayer.displayPlaceholder { + component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer, false) + } + } + + if !foundExactItem { + if let (item, itemKey) = self.item(atPoint: point, extendedHitRange: true), let itemLayer = self.visibleItemLayers[itemKey] { + foundItem = true + if !itemLayer.displayPlaceholder { + component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer, false) + } + } + } + + let _ = foundItem + } + self.addGestureRecognizer(contextGesture) + + //self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))) longTapRecognizer.minimumPressDuration = 0.2 @@ -2782,6 +3022,14 @@ public final class EmojiPagerContentComponent: Component { return false } + public func wantsDisplayBelowKeyboard() -> Bool { + if let visibleSearchHeader = self.visibleSearchHeader { + return visibleSearchHeader.wantsDisplayBelowKeyboard + } else { + return false + } + } + public func animateIn(fromLocation: CGPoint) { let scrollLocation = self.convert(fromLocation, to: self.scrollView) for (key, itemLayer) in self.visibleItemLayers { @@ -3352,11 +3600,11 @@ public final class EmojiPagerContentComponent: Component { } @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - guard let component = self.component else { + guard let _ = self.component else { return } if case .ended = recognizer.state { - let locationInScrollView = recognizer.location(in: self.scrollView) + /*let locationInScrollView = recognizer.location(in: self.scrollView) outer: for (id, groupHeader) in self.visibleGroupHeaders { if groupHeader.frame.insetBy(dx: -10.0, dy: -6.0).contains(locationInScrollView) { let groupHeaderPoint = self.scrollView.convert(locationInScrollView, to: groupHeader) @@ -3390,7 +3638,7 @@ public final class EmojiPagerContentComponent: Component { } } - let _ = foundItem + let _ = foundItem*/ } } @@ -3611,7 +3859,7 @@ public final class EmojiPagerContentComponent: Component { } private func updateScrollingOffset(isReset: Bool, transition: Transition) { - guard let component = self.component else { + guard let _ = self.component else { return } @@ -3622,7 +3870,7 @@ public final class EmojiPagerContentComponent: Component { let offsetToBottomEdge = max(0.0, scrollView.contentSize.height - currentBounds.maxY) let relativeOffset = scrollView.contentOffset.y - previousScrollingOffsetValue.value - if case .detailed = component.itemLayoutType { + //if case .detailed = component.itemLayoutType { self.pagerEnvironment?.onChildScrollingUpdate(PagerComponentChildEnvironment.ContentScrollingUpdate( relativeOffset: relativeOffset, absoluteOffsetToTopEdge: offsetToTopEdge, @@ -3631,7 +3879,7 @@ public final class EmojiPagerContentComponent: Component { isInteracting: isInteracting, transition: transition )) - } + //} } self.previousScrollingOffset = ScrollingOffsetState(value: scrollView.contentOffset.y, isDraggingOrDecelerating: isInteracting) } @@ -3698,6 +3946,33 @@ public final class EmojiPagerContentComponent: Component { } } + if component.displaySearch { + let visibleSearchHeader: SearchHeaderView + if let current = self.visibleSearchHeader { + visibleSearchHeader = current + } else { + visibleSearchHeader = SearchHeaderView(requestUpdate: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate) + }) + self.visibleSearchHeader = visibleSearchHeader + self.scrollView.addSubview(visibleSearchHeader) + self.mirrorContentScrollView.layer.addSublayer(visibleSearchHeader.tintContainerLayer) + } + + let searchHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.searchInsets.left, y: itemLayout.searchInsets.top), size: CGSize(width: itemLayout.width - itemLayout.searchInsets.left - itemLayout.searchInsets.right, height: itemLayout.searchHeight)) + visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, useOpaqueTheme: useOpaqueTheme, title: keyboardChildEnvironment.strings.Common_Search, size: searchHeaderFrame.size, transition: transition) + transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame) + } else { + if let visibleSearchHeader = self.visibleSearchHeader { + self.visibleSearchHeader = nil + visibleSearchHeader.removeFromSuperview() + visibleSearchHeader.tintContainerLayer.removeFromSuperlayer() + } + } + for groupItems in itemLayout.visibleItems(for: effectiveVisibleBounds) { let itemGroup = component.itemGroups[groupItems.groupIndex] let itemGroupLayout = itemLayout.itemGroupLayouts[groupItems.groupIndex] @@ -4171,7 +4446,7 @@ public final class EmojiPagerContentComponent: Component { placeholderView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } - itemLayer.isVisibleForAnimations = true + itemLayer.isVisibleForAnimations = keyboardChildEnvironment.isContentInFocus } } } @@ -4701,6 +4976,7 @@ public final class EmojiPagerContentComponent: Component { itemGroups: itemGroups, expandedGroupIds: self.expandedGroupIds, curveNearBounds: component.warpContentsOnEdges, + displaySearch: component.displaySearch, customLayout: component.inputInteractionHolder.inputInteraction?.customLayout ) if let previousItemLayout = self.itemLayout { @@ -5362,29 +5638,6 @@ public final class EmojiPagerContentComponent: Component { } } - if areUnicodeEmojiEnabled { - for (subgroupId, list) in staticEmojiMapping { - let groupId: AnyHashable = "static" - for emojiString in list { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .staticEmoji(emojiString), - itemFile: nil, - subgroupId: subgroupId.rawValue, - icon: .none, - accentTint: false - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } - } - var installedCollectionIds = Set() for (id, _, _) in view.collectionInfos { installedCollectionIds.insert(id) @@ -5514,6 +5767,29 @@ public final class EmojiPagerContentComponent: Component { } } + if areUnicodeEmojiEnabled { + for (subgroupId, list) in staticEmojiMapping { + let groupId: AnyHashable = "static" + for emojiString in list { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .staticEmoji(emojiString), + itemFile: nil, + subgroupId: subgroupId.rawValue, + icon: .none, + accentTint: false + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } + return EmojiPagerContentComponent( id: "emoji", context: context, @@ -5557,6 +5833,7 @@ public final class EmojiPagerContentComponent: Component { }, itemLayoutType: .compact, warpContentsOnEdges: isReactionSelection || isStatusSelection, + displaySearch: false, enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection, selectedItems: selectedItems ) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index f039db7add..5a72b21aa4 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -14,15 +14,18 @@ import LocalizedPeerData public final class EntityKeyboardChildEnvironment: Equatable { public let theme: PresentationTheme public let strings: PresentationStrings + public let isContentInFocus: Bool public let getContentActiveItemUpdated: (AnyHashable) -> ActionSlot<(AnyHashable, AnyHashable?, Transition)>? public init( theme: PresentationTheme, strings: PresentationStrings, + isContentInFocus: Bool, getContentActiveItemUpdated: @escaping (AnyHashable) -> ActionSlot<(AnyHashable, AnyHashable?, Transition)>? ) { self.theme = theme self.strings = strings + self.isContentInFocus = isContentInFocus self.getContentActiveItemUpdated = getContentActiveItemUpdated } @@ -33,6 +36,9 @@ public final class EntityKeyboardChildEnvironment: Equatable { if lhs.strings !== rhs.strings { return false } + if lhs.isContentInFocus != rhs.isContentInFocus { + return false + } return true } @@ -81,6 +87,7 @@ public final class EntityKeyboardComponent: Component { public let theme: PresentationTheme public let strings: PresentationStrings + public let isContentInFocus: Bool public let containerInsets: UIEdgeInsets public let topPanelInsets: UIEdgeInsets public let emojiContent: EmojiPagerContentComponent @@ -104,6 +111,7 @@ public final class EntityKeyboardComponent: Component { public init( theme: PresentationTheme, strings: PresentationStrings, + isContentInFocus: Bool, containerInsets: UIEdgeInsets, topPanelInsets: UIEdgeInsets, emojiContent: EmojiPagerContentComponent, @@ -126,6 +134,7 @@ public final class EntityKeyboardComponent: Component { ) { self.theme = theme self.strings = strings + self.isContentInFocus = isContentInFocus self.containerInsets = containerInsets self.topPanelInsets = topPanelInsets self.emojiContent = emojiContent @@ -154,6 +163,9 @@ public final class EntityKeyboardComponent: Component { if lhs.strings !== rhs.strings { return false } + if lhs.isContentInFocus != rhs.isContentInFocus { + return false + } if lhs.containerInsets != rhs.containerInsets { return false } @@ -620,6 +632,7 @@ public final class EntityKeyboardComponent: Component { EntityKeyboardChildEnvironment( theme: component.theme, strings: component.strings, + isContentInFocus: component.isContentInFocus, getContentActiveItemUpdated: { id in if id == AnyHashable("gifs") { return gifsContentItemIdUpdated diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 8328bb517d..2a96109768 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -1760,7 +1760,16 @@ public final class EntityKeyboardTopPanelComponent: Component { var validIds = Set() let visibleItemRange = itemLayout.visibleItemRange(for: visibleBounds) if !self.items.isEmpty && visibleItemRange.maxIndex >= visibleItemRange.minIndex { - for index in visibleItemRange.minIndex ... visibleItemRange.maxIndex { + var indices = Array(visibleItemRange.minIndex ... visibleItemRange.maxIndex) + for i in 0 ..< self.items.count { + if self.items[i].id == AnyHashable("static") { + if !indices.contains(i) { + indices.append(i) + } + break + } + } + for index in indices { let item = self.items[index] validIds.insert(item.id) @@ -1911,6 +1920,9 @@ public final class EntityKeyboardTopPanelComponent: Component { itemIndex = component.items.count - 1 useRightAnchor = true } + if itemIndex == component.items.count - 1 { + useRightAnchor = true + } if newBounds.minX < 0.0 { newBounds.origin.x = 0.0 itemIndex = 0 @@ -1918,12 +1930,8 @@ public final class EntityKeyboardTopPanelComponent: Component { } if useRightAnchor { - var newBounds = CGRect(origin: CGPoint(x: updatedItemFrame.maxX - previousDistanceToItemRight, y: 0.0), size: availableSize) - if newBounds.minX > itemLayout.contentSize.width - self.scrollView.bounds.width { - newBounds.origin.x = itemLayout.contentSize.width - self.scrollView.bounds.width - } - if newBounds.minX < 0.0 { - } + let _ = previousDistanceToItemRight + newBounds.origin.x = itemLayout.contentSize.width - self.scrollView.bounds.width } previousItemFrame = previousItemLayout.containerFrame(at: itemIndex) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 5cc319cf34..4c3c430498 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1495,6 +1495,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G items.tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(packReferences.count)), arguments: nil, file: nil, action: action) } else if let reference = packReferences.first { items.tipSignal = context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false) + |> delay(1.0, queue: .mainQueue()) |> filter { result in if case .result = result { return true @@ -1505,7 +1506,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> mapToSignal { result -> Signal in if case let .result(info, items, _) = result, let presentationContext = presentationContext { let tip: ContextController.Tip = .animatedEmoji( - text: presentationData.strings.ChatContextMenu_EmojiSetSingle(info.title).string, + text: presentationData.strings.ChatContextMenu_ReactionEmojiSetSingle(info.title).string, arguments: TextNodeWithEntities.Arguments( context: context, cache: presentationContext.animationCache, diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index b0b2a01aa2..29ea387cfd 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -762,6 +762,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.isInFocus = isInFocus self.inputMediaNode?.simulateUpdateLayout(isVisible: isInFocus) + if let inputNode = self.inputNode as? ChatEntityKeyboardInputNode { + inputNode.simulateUpdateLayout(isVisible: isInFocus) + } } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, ContainedViewLayoutTransition) -> Void) { diff --git a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift index 17b614b533..a723ac7633 100644 --- a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift @@ -195,7 +195,30 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { continue } - let animationData = EntityKeyboardAnimationData(file: item.file) + let animationData: EntityKeyboardAnimationData + + if let thumbnail = featuredStickerPack.info.thumbnail { + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + animationData = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(featuredStickerPack.info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: featuredStickerPack.info.immediateThumbnailData, + isReaction: false + ) + } else { + animationData = EntityKeyboardAnimationData(file: item.file) + } + let resultItem = EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData), @@ -513,6 +536,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { }, itemLayoutType: .detailed, warpContentsOnEdges: false, + displaySearch: false, enableLongPress: false, selectedItems: Set() ) @@ -1094,6 +1118,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { }, navigationController: { [weak controllerInteraction] in return controllerInteraction?.navigationController() + }, + requestUpdate: { _ in + }, sendSticker: { [weak controllerInteraction] fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets in guard let controllerInteraction = controllerInteraction else { @@ -1300,6 +1327,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { }, navigationController: { [weak controllerInteraction] in return controllerInteraction?.navigationController() + }, + requestUpdate: { _ in + }, sendSticker: { [weak controllerInteraction] fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets in guard let controllerInteraction = controllerInteraction else { @@ -1465,6 +1495,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) } + func simulateUpdateLayout(isVisible: Bool) { + guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _, isExpanded) = self.currentState else { + return + } + let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) + } + override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) @@ -1526,6 +1563,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { component: AnyComponent(EntityKeyboardComponent( theme: interfaceState.theme, strings: interfaceState.strings, + isContentInFocus: isVisible, containerInsets: UIEdgeInsets(top: 0.0, left: leftInset, bottom: bottomInset, right: rightInset), topPanelInsets: UIEdgeInsets(), emojiContent: self.currentInputData.emoji, @@ -1636,7 +1674,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { for group in itemGroups { if !(group.groupId.base is ItemCollectionId) { - updatedGroups.append(group) + if group.groupId != AnyHashable("static") { + updatedGroups.append(group) + } } else { if group.isEmbedded { continue @@ -2016,6 +2056,9 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV }, navigationController: { return nil + }, + requestUpdate: { _ in + }, sendSticker: nil, chatPeerId: nil, diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 4c5d04d298..9aa2a58160 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1595,7 +1595,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if customReactionEmojiPacks.count == 1, let firstCustomEmojiReaction = firstCustomEmojiReaction { tip = .animatedEmoji( - text: presentationData.strings.ChatContextMenu_EmojiSetSingle(customReactionEmojiPacks[0].title).string, + text: presentationData.strings.ChatContextMenu_ReactionEmojiSetSingle(customReactionEmojiPacks[0].title).string, arguments: TextNodeWithEntities.Arguments( context: context, cache: controllerInteraction.presentationContext.animationCache, @@ -1609,7 +1609,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } ) } else if customReactionEmojiPacks.count > 1 { - tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(customReactionEmojiPacks.count)), arguments: nil, file: nil, action: { + tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_ReactionEmojiSet(Int32(customReactionEmojiPacks.count)), arguments: nil, file: nil, action: { (interfaceInteraction.chatController() as? ChatControllerImpl)?.presentEmojiList(references: customReactionEmojiPacks.map { pack -> StickerPackReference in .id(id: pack.id.id, accessHash: pack.accessHash) }) }) } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 0530ebeaa7..5e9e72e469 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2543,7 +2543,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.currentEmojiSuggestionView = nil currentEmojiSuggestionView.alpha = 0.0 - currentEmojiSuggestionView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, completion: { [weak currentEmojiSuggestionView] _ in + currentEmojiSuggestionView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak currentEmojiSuggestionView] _ in currentEmojiSuggestionView?.removeFromSuperview() }) } diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 7a173b3c29..c49d65a7fe 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -675,9 +675,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView { case .verified: titleCredibilityContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor) case .fake: - titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_ScamAccount.uppercased()) - case .scam: titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased()) + case .scam: + titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_ScamAccount.uppercased()) case let .emojiStatus(emojiStatus): titleCredibilityContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor, themeColor: self.theme.list.itemAccentColor, loopMode: .count(2)) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 316df3885f..d8ff7bbfba 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2346,10 +2346,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor) emojiExpandedStatusContent = .verified(fillColor: UIColor(rgb: 0xffffff, alpha: 0.75), foregroundColor: .clear) case .fake: - emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) + emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) emojiExpandedStatusContent = emojiRegularStatusContent case .scam: - emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) + emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) emojiExpandedStatusContent = emojiRegularStatusContent case let .emojiStatus(emojiStatus): currentEmojiStatus = emojiStatus