Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2022-07-06 13:00:55 +02:00
commit 907918cead
52 changed files with 1831 additions and 319 deletions

View File

@ -352,7 +352,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
guard let presentationInterfaceState = self.presentationInterfaceState else {
return 0.0
}
return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact))
return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), isMediaInputExpanded: false)
}
public func setCaption(_ caption: NSAttributedString?) {
@ -496,7 +496,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
return minimalHeight
}
public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
let hadLayout = self.validLayout != nil
let previousAdditionalSideInsets = self.validLayout?.3
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
@ -1110,7 +1110,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
self.focusUpdated?(true)
if self.isCaption, let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let presentationInterfaceState = self.presentationInterfaceState {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: false)
}
}
@ -1121,7 +1121,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
self.focusUpdated?(false)
if self.isCaption, let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let presentationInterfaceState = self.presentationInterfaceState {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: false)
}
}

View File

@ -1098,7 +1098,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
if textInputPanelNode.frame.width.isZero {
panelTransition = .immediate
}
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left + layout.safeInsets.left, rightInset: insets.right + layout.safeInsets.right, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left + layout.safeInsets.left, rightInset: insets.right + layout.safeInsets.right, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: false)
let panelFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: panelHeight)
if textInputPanelNode.frame.width.isZero {
textInputPanelNode.frame = panelFrame

View File

@ -1312,6 +1312,8 @@ public final class ChatListNode: ListView {
switch activity {
case .interactingWithEmoji:
return true
case .speakingInGroupCall:
return true
default:
return false
}
@ -1343,9 +1345,9 @@ public final class ChatListNode: ListView {
return engine.data.get(EngineDataMap(
activitiesByPeerId.keys.filter { key in
if case .global = key.category {
return false
} else {
return true
} else {
return false
}
}.map { key in
return TelegramEngine.EngineData.Item.Peer.Peer(id: key.peerId)

View File

@ -3,13 +3,42 @@ import AccountContext
import SwiftSignalKit
public enum ChatTextInputAccessoryItem: Equatable {
public enum Key: Hashable {
case keyboard
case stickers(Bool)
case stickers
case inputButtons
case commands
case silentPost
case messageAutoremoveTimeout
case scheduledMessages
}
case keyboard
case stickers(isEnabled: Bool, isEmoji: Bool)
case inputButtons
case commands
case silentPost(Bool)
case messageAutoremoveTimeout(Int32?)
case scheduledMessages
public var key: Key {
switch self {
case .keyboard:
return .keyboard
case .stickers:
return .stickers
case .inputButtons:
return .inputButtons
case .commands:
return .commands
case .silentPost:
return .silentPost
case .messageAutoremoveTimeout:
return .messageAutoremoveTimeout
case .scheduledMessages:
return .scheduledMessages
}
}
}
public final class InstantVideoControllerRecordingStatus {

View File

@ -128,6 +128,16 @@ private extension Transition.Animation.Curve {
}
}
public extension Transition.Animation {
var isImmediate: Bool {
if case .none = self {
return true
} else {
return false
}
}
}
public struct Transition {
public enum Animation {
public enum Curve {
@ -245,6 +255,61 @@ public struct Transition {
}
}
public func setBounds(layer: CALayer, bounds: CGRect, completion: ((Bool) -> Void)? = nil) {
if layer.bounds == bounds {
completion?(true)
return
}
switch self.animation {
case .none:
layer.bounds = bounds
layer.removeAnimation(forKey: "bounds")
completion?(true)
case .curve:
let previousBounds = layer.presentation()?.bounds ?? layer.bounds
layer.bounds = bounds
self.animateBounds(layer: layer, from: previousBounds, to: layer.bounds, completion: completion)
}
}
public func setPosition(layer: CALayer, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if layer.position == position {
completion?(true)
return
}
switch self.animation {
case .none:
layer.position = position
layer.removeAnimation(forKey: "position")
completion?(true)
case .curve:
let previousPosition = layer.presentation()?.position ?? layer.position
layer.position = position
self.animatePosition(layer: layer, from: previousPosition, to: layer.position, completion: completion)
}
}
public func attachAnimation(view: UIView, completion: @escaping (Bool) -> Void) {
switch self.animation {
case .none:
completion(true)
case let .curve(duration, curve):
view.layer.animate(
from: 0.0 as NSNumber,
to: 1.0 as NSNumber,
keyPath: "attached\(UInt32.random(in: 0 ... UInt32.max))",
duration: duration,
delay: 0.0,
curve: curve,
removeOnCompletion: true,
additive: true,
completion: completion
)
}
}
public func setAlpha(view: UIView, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
if view.alpha == alpha {
completion?(true)
@ -356,11 +421,19 @@ public struct Transition {
}
public func animatePosition(view: UIView, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
self.animatePosition(layer: view.layer, from: fromValue, to: toValue, additive: additive, completion: completion)
}
public func animateBounds(view: UIView, from fromValue: CGRect, to toValue: CGRect, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
self.animateBounds(layer: view.layer, from: fromValue, to: toValue, additive: additive, completion: completion)
}
public func animatePosition(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self.animation {
case .none:
completion?(true)
case let .curve(duration, curve):
view.layer.animate(
layer.animate(
from: NSValue(cgPoint: fromValue),
to: NSValue(cgPoint: toValue),
keyPath: "position",
@ -374,12 +447,12 @@ public struct Transition {
}
}
public func animateBounds(view: UIView, from fromValue: CGRect, to toValue: CGRect, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
public func animateBounds(layer: CALayer, from fromValue: CGRect, to toValue: CGRect, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self.animation {
case .none:
break
case let .curve(duration, curve):
view.layer.animate(
layer.animate(
from: NSValue(cgRect: fromValue),
to: NSValue(cgRect: toValue),
keyPath: "bounds",

View File

@ -7,6 +7,7 @@ public final class Button: Component {
public let tag: AnyObject?
public let automaticHighlight: Bool
public let action: () -> Void
public let holdAction: (() -> Void)?
convenience public init(
content: AnyComponent<Empty>,
@ -17,7 +18,8 @@ public final class Button: Component {
minSize: nil,
tag: nil,
automaticHighlight: true,
action: action
action: action,
holdAction: nil
)
}
@ -26,13 +28,15 @@ public final class Button: Component {
minSize: CGSize? = nil,
tag: AnyObject? = nil,
automaticHighlight: Bool = true,
action: @escaping () -> Void
action: @escaping () -> Void,
holdAction: (() -> Void)?
) {
self.content = content
self.minSize = minSize
self.tag = tag
self.automaticHighlight = automaticHighlight
self.action = action
self.holdAction = holdAction
}
public func minSize(_ minSize: CGSize?) -> Button {
@ -41,7 +45,19 @@ public final class Button: Component {
minSize: minSize,
tag: self.tag,
automaticHighlight: self.automaticHighlight,
action: self.action
action: self.action,
holdAction: self.holdAction
)
}
public func withHoldAction(_ holdAction: (() -> Void)?) -> Button {
return Button(
content: self.content,
minSize: self.minSize,
tag: self.tag,
automaticHighlight: self.automaticHighlight,
action: self.action,
holdAction: holdAction
)
}
@ -51,7 +67,8 @@ public final class Button: Component {
minSize: self.minSize,
tag: tag,
automaticHighlight: self.automaticHighlight,
action: self.action
action: self.action,
holdAction: self.holdAction
)
}
@ -86,6 +103,9 @@ public final class Button: Component {
}
}
private var holdActionTriggerred: Bool = false
private var holdActionTimer: Timer?
override init(frame: CGRect) {
self.contentView = ComponentHostView<Empty>()
self.contentView.isUserInteractionEnabled = false
@ -101,6 +121,10 @@ public final class Button: Component {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.holdActionTimer?.invalidate()
}
public func matches(tag: Any) -> Bool {
if let component = self.component, let componentTag = component.tag {
let tag = tag as AnyObject
@ -112,24 +136,69 @@ public final class Button: Component {
}
@objc private func pressed() {
if self.holdActionTriggerred {
self.holdActionTriggerred = false
} else {
self.component?.action()
}
}
override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
self.currentIsHighlighted = true
self.holdActionTriggerred = false
if self.component?.holdAction != nil {
self.holdActionTriggerred = true
self.component?.action()
self.holdActionTimer?.invalidate()
if #available(iOS 10.0, *) {
let holdActionTimer = Timer(timeInterval: 1.0, repeats: false, block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.holdActionTimer?.invalidate()
strongSelf.component?.holdAction?()
strongSelf.beginExecuteHoldActionTimer()
})
self.holdActionTimer = holdActionTimer
RunLoop.main.add(holdActionTimer, forMode: .common)
}
}
return super.beginTracking(touch, with: event)
}
private func beginExecuteHoldActionTimer() {
self.holdActionTimer?.invalidate()
if #available(iOS 10.0, *) {
let holdActionTimer = Timer(timeInterval: 0.2, repeats: true, block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.component?.holdAction?()
})
self.holdActionTimer = holdActionTimer
RunLoop.main.add(holdActionTimer, forMode: .common)
}
}
override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
self.currentIsHighlighted = false
self.holdActionTimer?.invalidate()
self.holdActionTimer = nil
super.endTracking(touch, with: event)
}
override public func cancelTracking(with event: UIEvent?) {
self.currentIsHighlighted = false
self.holdActionTimer?.invalidate()
self.holdActionTimer = nil
super.cancelTracking(with: event)
}

View File

@ -106,9 +106,22 @@ public final class ComponentHostView<EnvironmentType>: UIView {
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if self.alpha.isZero {
return nil
}
for view in self.subviews.reversed() {
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result
}
}
let result = super.hitTest(point, with: event)
if result != self {
return result
} else {
return nil
}
}
public func findTaggedView(tag: Any) -> UIView? {
guard let componentView = self.componentView else {

View File

@ -157,9 +157,6 @@ public final class LottieAnimationComponent: Component {
for (key, value) in component.animation.colors {
view.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
/*let colorCallback = LOTColorValueCallback(color: value.cgColor)
self.colorCallbacks.append(colorCallback)
view.setValueDelegate(colorCallback, for: LOTKeypath(string: "\(key).Color"))*/
}
self.animationView = view

View File

@ -9,11 +9,15 @@ public protocol PagerExpandableScrollView: UIScrollView {
public protocol PagerPanGestureRecognizer: UIGestureRecognizer {
}
open class PagerExternalTopPanelContainer: SparseContainerView {
}
public final class PagerComponentChildEnvironment: Equatable {
public struct ContentScrollingUpdate {
public var relativeOffset: CGFloat
public var absoluteOffsetToTopEdge: CGFloat?
public var absoluteOffsetToBottomEdge: CGFloat?
public var isReset: Bool
public var isInteracting: Bool
public var transition: Transition
@ -21,12 +25,14 @@ public final class PagerComponentChildEnvironment: Equatable {
relativeOffset: CGFloat,
absoluteOffsetToTopEdge: CGFloat?,
absoluteOffsetToBottomEdge: CGFloat?,
isReset: Bool,
isInteracting: Bool,
transition: Transition
) {
self.relativeOffset = relativeOffset
self.absoluteOffsetToTopEdge = absoluteOffsetToTopEdge
self.absoluteOffsetToBottomEdge = absoluteOffsetToBottomEdge
self.isReset = isReset
self.isInteracting = isInteracting
self.transition = transition
}
@ -61,6 +67,7 @@ public final class PagerComponentPanelEnvironment<TopPanelEnvironment>: Equatabl
public let activeContentId: AnyHashable?
public let navigateToContentId: (AnyHashable) -> Void
public let visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
public let isExpandedUpdated: (Bool, Transition) -> Void
init(
contentOffset: CGFloat,
@ -70,7 +77,8 @@ public final class PagerComponentPanelEnvironment<TopPanelEnvironment>: Equatabl
contentAccessoryRightButtons: [AnyComponentWithIdentity<Empty>],
activeContentId: AnyHashable?,
navigateToContentId: @escaping (AnyHashable) -> Void,
visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>,
isExpandedUpdated: @escaping (Bool, Transition) -> Void
) {
self.contentOffset = contentOffset
self.contentTopPanels = contentTopPanels
@ -80,6 +88,7 @@ public final class PagerComponentPanelEnvironment<TopPanelEnvironment>: Equatabl
self.activeContentId = activeContentId
self.navigateToContentId = navigateToContentId
self.visibilityFractionUpdated = visibilityFractionUpdated
self.isExpandedUpdated = isExpandedUpdated
}
public static func ==(lhs: PagerComponentPanelEnvironment, rhs: PagerComponentPanelEnvironment) -> Bool {
@ -122,6 +131,12 @@ public final class PagerComponentViewTag {
}
}
public enum PagerComponentPanelHideBehavior {
case hideOnScroll
case show
case hide
}
public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvironment: Equatable>: Component {
public typealias EnvironmentType = ChildEnvironmentType
@ -134,10 +149,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
public let defaultId: AnyHashable?
public let contentBackground: AnyComponent<Empty>?
public let topPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?
public let externalTopPanelContainer: UIView?
public let externalTopPanelContainer: PagerExternalTopPanelContainer?
public let bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?
public let panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)?
public let hidePanels: Bool
public let isTopPanelExpandedUpdated: (Bool, Transition) -> Void
public let panelHideBehavior: PagerComponentPanelHideBehavior
public init(
contentInsets: UIEdgeInsets,
@ -149,10 +165,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
defaultId: AnyHashable?,
contentBackground: AnyComponent<Empty>?,
topPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?,
externalTopPanelContainer: UIView?,
externalTopPanelContainer: PagerExternalTopPanelContainer?,
bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?,
panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)?,
hidePanels: Bool
isTopPanelExpandedUpdated: @escaping (Bool, Transition) -> Void,
panelHideBehavior: PagerComponentPanelHideBehavior
) {
self.contentInsets = contentInsets
self.contents = contents
@ -166,7 +183,8 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.externalTopPanelContainer = externalTopPanelContainer
self.bottomPanel = bottomPanel
self.panelStateUpdated = panelStateUpdated
self.hidePanels = hidePanels
self.isTopPanelExpandedUpdated = isTopPanelExpandedUpdated
self.panelHideBehavior = panelHideBehavior
}
public static func ==(lhs: PagerComponent, rhs: PagerComponent) -> Bool {
@ -197,7 +215,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
if lhs.bottomPanel != rhs.bottomPanel {
return false
}
if lhs.hidePanels != rhs.hidePanels {
if lhs.panelHideBehavior != rhs.panelHideBehavior {
return false
}
@ -241,6 +259,8 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
private var panRecognizer: PagerPanGestureRecognizerImpl?
private var isTopPanelExpanded: Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
@ -309,6 +329,13 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
}
func update(component: PagerComponent<ChildEnvironmentType, TopPanelEnvironment>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let previousPanelHideBehavior = self.component?.panelHideBehavior
var panelStateTransition = transition
if let previousPanelHideBehavior = previousPanelHideBehavior, previousPanelHideBehavior != component.panelHideBehavior, panelStateTransition.animation.isImmediate {
panelStateTransition = Transition(animation: .curve(duration: 0.3, curve: .spring))
}
self.component = component
self.state = state
@ -345,7 +372,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var contentInsets = component.contentInsets
var scrollingPanelOffsetFraction: CGFloat
if let centralId = centralId, let centralContentView = self.contentViews[centralId] {
if case .show = component.panelHideBehavior {
scrollingPanelOffsetFraction = 0.0
} else if let centralId = centralId, let centralContentView = self.contentViews[centralId] {
scrollingPanelOffsetFraction = centralContentView.scrollingPanelOffsetFraction
} else {
scrollingPanelOffsetFraction = 0.0
@ -354,13 +383,12 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var topPanelHeight: CGFloat = 0.0
if let topPanel = component.topPanel {
let topPanelView: ComponentHostView<PagerComponentPanelEnvironment<TopPanelEnvironment>>
var topPanelTransition = transition
var topPanelTransition = panelStateTransition
if let current = self.topPanelView {
topPanelView = current
} else {
topPanelTransition = .immediate
topPanelView = ComponentHostView<PagerComponentPanelEnvironment<TopPanelEnvironment>>()
topPanelView.clipsToBounds = true
self.topPanelView = topPanelView
}
@ -381,7 +409,13 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
contentAccessoryRightButtons: [],
activeContentId: centralId,
navigateToContentId: navigateToContentId,
visibilityFractionUpdated: self.topPanelVisibilityFractionUpdated
visibilityFractionUpdated: self.topPanelVisibilityFractionUpdated,
isExpandedUpdated: { [weak self] isExpanded, transition in
guard let strongSelf = self else {
return
}
strongSelf.isTopPanelExpandedUpdated(isExpanded: isExpanded, transition: transition)
}
)
},
containerSize: availableSize
@ -392,26 +426,31 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var topPanelOffset = topPanelSize.height * scrollingPanelOffsetFraction
var topPanelVisibilityFraction: CGFloat = 1.0 - scrollingPanelOffsetFraction
if component.hidePanels {
switch component.panelHideBehavior {
case .hide:
topPanelVisibilityFraction = 0.0
case .show:
topPanelVisibilityFraction = 1.0
case .hideOnScroll:
break
}
self.topPanelVisibilityFractionUpdated.invoke((topPanelVisibilityFraction, topPanelTransition))
topPanelHeight = max(0.0, topPanelSize.height - topPanelOffset)
if component.hidePanels {
if case .hide = component.panelHideBehavior {
topPanelOffset = topPanelSize.height
}
if component.externalTopPanelContainer != nil {
var visibleTopPanelHeight = max(0.0, topPanelSize.height - topPanelOffset)
if component.hidePanels {
if case .hide = component.panelHideBehavior {
visibleTopPanelHeight = 0.0
}
transition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(), size: CGSize(width: topPanelSize.width, height: visibleTopPanelHeight)))
panelStateTransition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(), size: CGSize(width: topPanelSize.width, height: visibleTopPanelHeight)))
} else {
transition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelOffset), size: topPanelSize))
panelStateTransition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelOffset), size: topPanelSize))
}
contentInsets.top += topPanelSize.height
@ -428,7 +467,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var bottomPanelOffset: CGFloat = 0.0
if let bottomPanel = component.bottomPanel {
let bottomPanelView: ComponentHostView<PagerComponentPanelEnvironment<TopPanelEnvironment>>
var bottomPanelTransition = transition
var bottomPanelTransition = panelStateTransition
if let current = self.bottomPanelView {
bottomPanelView = current
} else {
@ -449,7 +488,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
contentAccessoryRightButtons: component.contentAccessoryRightButtons,
activeContentId: centralId,
navigateToContentId: navigateToContentId,
visibilityFractionUpdated: self.bottomPanelVisibilityFractionUpdated
visibilityFractionUpdated: self.bottomPanelVisibilityFractionUpdated,
isExpandedUpdated: { _, _ in
}
)
},
containerSize: availableSize
@ -458,11 +499,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.bottomPanelHeight = bottomPanelSize.height
bottomPanelOffset = bottomPanelSize.height * scrollingPanelOffsetFraction
if component.hidePanels {
if case .hide = component.panelHideBehavior {
bottomPanelOffset = bottomPanelSize.height
}
transition.setFrame(view: bottomPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height + bottomPanelOffset), size: bottomPanelSize))
panelStateTransition.setFrame(view: bottomPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height + bottomPanelOffset), size: bottomPanelSize))
contentInsets.bottom += bottomPanelSize.height
} else {
@ -475,11 +516,17 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.bottomPanelHeight = 0.0
}
let effectiveTopPanelHeight: CGFloat = component.hidePanels ? 0.0 : topPanelHeight
let effectiveTopPanelHeight: CGFloat
switch component.panelHideBehavior {
case .hide:
effectiveTopPanelHeight = 0.0
case .show, .hideOnScroll:
effectiveTopPanelHeight = topPanelHeight
}
if let contentBackground = component.contentBackground {
let contentBackgroundView: ComponentHostView<Empty>
var contentBackgroundTransition = transition
var contentBackgroundTransition = panelStateTransition
if let current = self.contentBackgroundView {
contentBackgroundView = current
} else {
@ -633,7 +680,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
PagerComponentPanelState(
topPanelHeight: topPanelHeight
),
transition
panelStateTransition
)
}
@ -647,6 +694,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var offsetDelta: CGFloat?
offsetDelta = (update.absoluteOffsetToTopEdge ?? 0.0) - contentView.scrollingPanelOffsetToTopEdge
if update.isReset {
offsetDelta = 0.0
}
contentView.scrollingPanelOffsetToTopEdge = update.absoluteOffsetToTopEdge ?? 0.0
contentView.scrollingPanelOffsetToBottomEdge = update.absoluteOffsetToBottomEdge ?? .greatestFiniteMagnitude
@ -682,10 +732,24 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
contentView.scrollingPanelOffsetFraction = scrollingPanelOffsetFraction
self.state?.updated(transition: transition)
}
if self.isTopPanelExpanded {
self.isTopPanelExpanded = false
self.component?.isTopPanelExpandedUpdated(self.isTopPanelExpanded, Transition(animation: .curve(duration: 0.25, curve: .easeInOut)))
}
}
}
private func isTopPanelExpandedUpdated(isExpanded: Bool, transition: Transition) {
if self.isTopPanelExpanded == isExpanded {
return
}
self.isTopPanelExpanded = isExpanded
self.component?.isTopPanelExpandedUpdated(self.isTopPanelExpanded, transition)
}
}
public func makeView() -> View {
return View(frame: CGRect())
}

View File

@ -36,7 +36,7 @@ public func cancelParentGestures(view: UIView, ignore: [UIGestureRecognizer] = [
node.highligthedChanged(false)
}
if let superview = view.superview {
cancelParentGestures(view: superview)
cancelParentGestures(view: superview, ignore: ignore)
}
}

View File

@ -16,6 +16,8 @@ public class PortalView {
if let portalSuperview = self.view.superview, let index = portalSuperview.subviews.firstIndex(of: self.view) {
portalSuperview.insertSubview(self.view, at: index)
} else if let portalSuperlayer = self.view.layer.superlayer, let index = portalSuperlayer.sublayers?.firstIndex(of: self.view.layer) {
portalSuperlayer.insertSublayer(self.view.layer, at: UInt32(index))
}
}
}

View File

@ -1435,6 +1435,10 @@ public class TextNode: ASDisplayNode {
for run in glyphRuns {
let run = run as! CTRun
let glyphCount = CTRunGetGlyphCount(run)
let attributes = CTRunGetAttributes(run) as NSDictionary
if attributes["Attribute__EmbeddedItem"] != nil {
continue
}
CTRunDraw(run, context, CFRangeMake(0, glyphCount))
}
}

View File

@ -51,6 +51,9 @@ private func chatInputStateString(attributedString: NSAttributedString) -> NSAtt
if let _ = attributes[.underlineStyle] {
string.addAttribute(ChatTextInputAttributes.underline, value: true as NSNumber, range: range)
}
if let value = attributes[ChatTextInputAttributes.customEmoji] as? ChatTextInputTextCustomEmojiAttribute {
string.addAttribute(ChatTextInputAttributes.customEmoji, value: value, range: range)
}
})
return string
}

View File

@ -430,3 +430,69 @@ public final class ShimmerEffectNode: ASDisplayNode {
self.effectNode.frame = CGRect(origin: CGPoint(), size: size)
}
}
public final class StandaloneShimmerEffect {
private var image: UIImage?
private var background: UIColor?
private var foreground: UIColor?
public var layer: CALayer? {
didSet {
if self.layer !== oldValue {
self.updateLayer()
}
}
}
public init() {
}
public func update(background: UIColor, foreground: UIColor) {
if self.background == background && self.foreground == foreground {
return
}
self.background = background
self.foreground = foreground
self.image = generateImage(CGSize(width: 1.0, height: 320.0), opaque: false, scale: 1.0, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(background.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
context.clip(to: CGRect(origin: CGPoint(), size: size))
let transparentColor = foreground.withAlphaComponent(0.0).cgColor
let peakColor = foreground.cgColor
var locations: [CGFloat] = [0.0, 0.5, 1.0]
let colors: [CGColor] = [transparentColor, peakColor, transparentColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
self.updateLayer()
}
public func updateLayer() {
guard let layer = self.layer, let image = self.image else {
return
}
layer.contents = image.cgImage
if layer.animation(forKey: "shimmer") == nil {
let animation = CABasicAnimation(keyPath: "contentsRect.origin.y")
animation.fromValue = 1.0 as NSNumber
animation.toValue = -1.0 as NSNumber
animation.isAdditive = true
animation.repeatCount = .infinity
animation.duration = 0.8
animation.beginTime = layer.convertTime(1.0, from: nil)
layer.add(animation, forKey: "shimmer")
}
}
}

View File

@ -21,8 +21,8 @@ private func decodeStickerThumbnailData(_ data: Data) -> String {
return string
}
public func generateStickerPlaceholderImage(data: Data?, size: CGSize, imageSize: CGSize, backgroundColor: UIColor?, foregroundColor: UIColor) -> UIImage? {
return generateImage(size, rotatedContext: { size, context in
public func generateStickerPlaceholderImage(data: Data?, size: CGSize, scale: CGFloat? = nil, imageSize: CGSize, backgroundColor: UIColor?, foregroundColor: UIColor) -> UIImage? {
return generateImage(size, scale: scale, rotatedContext: { size, context in
if let backgroundColor = backgroundColor {
context.setFillColor(backgroundColor.cgColor)
context.setBlendMode(.copy)

View File

@ -291,6 +291,8 @@ swift_library(
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
"//submodules/Media/ConvertOpusToAAC:ConvertOpusToAAC",
"//submodules/Media/LocalAudioTranscription:LocalAudioTranscription",
"//submodules/Components/PagerComponent:PagerComponent",
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -7,21 +7,18 @@ import PagerComponent
private func traceScrollView(view: UIView, point: CGPoint) -> (UIScrollView?, Bool) {
for subview in view.subviews.reversed() {
let subviewPoint = view.convert(point, to: subview)
if subview.frame.contains(point) {
if subview.frame.contains(point) || subview is PagerExternalTopPanelContainer {
let (result, shouldContinue) = traceScrollView(view: subview, point: subviewPoint)
if let result = result {
return (result, false)
} else if subview.backgroundColor != nil {
return (nil, false)
} else if !shouldContinue{
} else if !shouldContinue {
return (nil, false)
}
}
}
if let scrollView = view as? UIScrollView {
if scrollView is ListViewScroller || scrollView is GridNodeScrollerView {
return (nil, false)
}
return (scrollView, false)
}
return (nil, true)
@ -31,6 +28,7 @@ private final class ExpansionPanRecognizer: UIGestureRecognizer, UIGestureRecogn
enum LockDirection {
case up
case down
case any
}
var requiredLockDirection: LockDirection = .up
@ -83,14 +81,23 @@ private final class ExpansionPanRecognizer: UIGestureRecognizer, UIGestureRecogn
var found = false
let point = touch.location(in: self.view)
if let _ = view.hitTest(point, with: event) as? UIButton {
} else if let scrollView = traceScrollView(view: view, point: point).0 {
let contentOffset = scrollView.contentOffset
let contentInset = scrollView.contentInset
if contentOffset.y.isLessThanOrEqualTo(contentInset.top) {
let hitView = view.hitTest(point, with: event)
if let _ = hitView as? UIButton {
} else if let hitView = hitView, hitView.asyncdisplaykit_node is ASButtonNode {
} else {
if let scrollView = traceScrollView(view: view, point: point).0 {
if scrollView is ListViewScroller || scrollView is GridNodeScrollerView {
found = false
} else {
found = true
}
} else {
found = true
}
}
if found {
self.beginPosition = point
} else {
@ -118,28 +125,46 @@ private final class ExpansionPanRecognizer: UIGestureRecognizer, UIGestureRecogn
}
var lockDirection: LockDirection?
let point = touch.location(in: self.view)
let tracedView = view.hitTest(point, with: event)
if let scrollView = traceScrollView(view: view, point: point).0 {
if !(scrollView is PagerExpandableScrollView) {
lockDirection = .any
} else {
let contentOffset = scrollView.contentOffset
let contentInset = scrollView.contentInset
if contentOffset.y <= contentInset.top {
lockDirection = self.requiredLockDirection
lockDirection = .down
}
}
} else {
lockDirection = .any
}
if let lockDirection = lockDirection {
if abs(translation.y) > 2.0 {
switch lockDirection {
case .up:
if translation.y < 0.0 {
if let tracedView = tracedView {
cancelParentGestures(view: tracedView, ignore: [self])
}
self.state = .began
} else {
self.state = .failed
}
case .down:
if translation.y > 0.0 {
if let tracedView = tracedView {
cancelParentGestures(view: tracedView, ignore: [self])
}
self.state = .began
} else {
self.state = .failed
}
case .any:
if let tracedView = tracedView {
cancelParentGestures(view: tracedView, ignore: [self])
}
self.state = .began
}
}
} else {
@ -179,6 +204,7 @@ public final class ChatInputPanelContainer: SparseNode, UIScrollViewDelegate {
private var scrollableDistance: CGFloat?
public private(set) var initialExpansionFraction: CGFloat = 0.0
public private(set) var expansionFraction: CGFloat = 0.0
public private(set) var stableIsExpanded: Bool = false
override public init() {
super.init()
@ -231,6 +257,8 @@ public final class ChatInputPanelContainer: SparseNode, UIScrollViewDelegate {
}
}
self.stableIsExpanded = self.expansionFraction == 1.0
if let expansionRecognizer = self.expansionRecognizer {
expansionRecognizer.requiredLockDirection = self.expansionFraction == 0.0 ? .up : .down
}
@ -250,10 +278,24 @@ public final class ChatInputPanelContainer: SparseNode, UIScrollViewDelegate {
public func expand() {
self.expansionFraction = 1.0
self.expansionRecognizer?.requiredLockDirection = self.expansionFraction == 0.0 ? .up : .down
self.stableIsExpanded = self.expansionFraction == 1.0
}
public func collapse() {
self.expansionFraction = 0.0
self.expansionRecognizer?.requiredLockDirection = self.expansionFraction == 0.0 ? .up : .down
self.stableIsExpanded = self.expansionFraction == 1.0
}
public func toggleIfEnabled() {
if let expansionRecognizer = self.expansionRecognizer, expansionRecognizer.isEnabled {
if self.expansionFraction == 0.0 {
self.expansionFraction = 1.0
} else {
self.expansionFraction = 0.0
}
self.stableIsExpanded = self.expansionFraction == 1.0
self.expansionUpdated?(.animated(duration: 0.4, curve: .spring))
}
}
}

View File

@ -38,6 +38,7 @@ swift_library(
"//submodules/PremiumUI:PremiumUI",
"//submodules/StickerPackPreviewUI:StickerPackPreviewUI",
"//submodules/UndoUI:UndoUI",
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
],
visibility = [
"//visibility:public",

View File

@ -230,7 +230,8 @@ public final class EmojiPagerContentComponent: Component {
var width: CGFloat
var containerInsets: UIEdgeInsets
var itemGroupLayouts: [ItemGroupLayout]
var itemSize: CGFloat
var nativeItemSize: CGFloat
let visibleItemSize: CGFloat
var horizontalSpacing: CGFloat
var verticalSpacing: CGFloat
var verticalGroupSpacing: CGFloat
@ -241,14 +242,17 @@ public final class EmojiPagerContentComponent: Component {
self.width = width
self.containerInsets = containerInsets
let minItemsPerRow: Int
let minSpacing: CGFloat
switch itemLayoutType {
case .compact:
self.itemSize = 36.0
minItemsPerRow = 8
self.nativeItemSize = 36.0
self.verticalSpacing = 9.0
minSpacing = 9.0
case .detailed:
self.itemSize = 76.0
minItemsPerRow = 5
self.nativeItemSize = 76.0
self.verticalSpacing = 2.0
minSpacing = 2.0
}
@ -257,8 +261,11 @@ public final class EmojiPagerContentComponent: Component {
let itemHorizontalSpace = width - self.containerInsets.left - self.containerInsets.right
self.itemsPerRow = Int((itemHorizontalSpace + minSpacing) / (self.itemSize + minSpacing))
self.horizontalSpacing = floor((itemHorizontalSpace - self.itemSize * CGFloat(self.itemsPerRow)) / CGFloat(self.itemsPerRow - 1))
self.itemsPerRow = max(minItemsPerRow, Int((itemHorizontalSpace + minSpacing) / (self.nativeItemSize + minSpacing)))
self.visibleItemSize = floor((itemHorizontalSpace - CGFloat(self.itemsPerRow - 1) * minSpacing) / CGFloat(self.itemsPerRow))
self.horizontalSpacing = floor((itemHorizontalSpace - self.visibleItemSize * CGFloat(self.itemsPerRow)) / CGFloat(self.itemsPerRow - 1))
var verticalGroupOrigin: CGFloat = self.containerInsets.top
self.itemGroupLayouts = []
@ -269,7 +276,7 @@ public final class EmojiPagerContentComponent: Component {
}
let numRowsInGroup = (itemGroup.itemCount + (self.itemsPerRow - 1)) / self.itemsPerRow
let groupContentSize = CGSize(width: width, height: itemTopOffset + CGFloat(numRowsInGroup) * self.itemSize + CGFloat(max(0, numRowsInGroup - 1)) * self.verticalSpacing)
let groupContentSize = CGSize(width: width, height: itemTopOffset + CGFloat(numRowsInGroup) * self.visibleItemSize + CGFloat(max(0, numRowsInGroup - 1)) * self.verticalSpacing)
self.itemGroupLayouts.append(ItemGroupLayout(
frame: CGRect(origin: CGPoint(x: 0.0, y: verticalGroupOrigin), size: groupContentSize),
id: itemGroup.id,
@ -290,12 +297,12 @@ public final class EmojiPagerContentComponent: Component {
return CGRect(
origin: CGPoint(
x: self.containerInsets.left + CGFloat(column) * (self.itemSize + self.horizontalSpacing),
y: groupLayout.frame.minY + groupLayout.itemTopOffset + CGFloat(row) * (self.itemSize + self.verticalSpacing)
x: self.containerInsets.left + CGFloat(column) * (self.visibleItemSize + self.horizontalSpacing),
y: groupLayout.frame.minY + groupLayout.itemTopOffset + CGFloat(row) * (self.visibleItemSize + self.verticalSpacing)
),
size: CGSize(
width: self.itemSize,
height: self.itemSize
width: self.visibleItemSize,
height: self.visibleItemSize
)
)
}
@ -310,9 +317,9 @@ public final class EmojiPagerContentComponent: Component {
continue
}
let offsetRect = rect.offsetBy(dx: -self.containerInsets.left, dy: -group.frame.minY - group.itemTopOffset)
var minVisibleRow = Int(floor((offsetRect.minY - self.verticalSpacing) / (self.itemSize + self.verticalSpacing)))
var minVisibleRow = Int(floor((offsetRect.minY - self.verticalSpacing) / (self.visibleItemSize + self.verticalSpacing)))
minVisibleRow = max(0, minVisibleRow)
let maxVisibleRow = Int(ceil((offsetRect.maxY - self.verticalSpacing) / (self.itemSize + self.verticalSpacing)))
let maxVisibleRow = Int(ceil((offsetRect.maxY - self.verticalSpacing) / (self.visibleItemSize + self.verticalSpacing)))
let minVisibleIndex = minVisibleRow * self.itemsPerRow
let maxVisibleIndex = min(group.itemCount - 1, (maxVisibleRow + 1) * self.itemsPerRow - 1)
@ -330,6 +337,60 @@ public final class EmojiPagerContentComponent: Component {
}
}
final class ItemPlaceholderView: UIView {
private let shimmerView: PortalSourceView?
private var placeholderView: PortalView?
private let placeholderMaskLayer: SimpleLayer
init(
context: AccountContext,
file: TelegramMediaFile,
shimmerView: PortalSourceView?,
color: UIColor?,
size: CGSize
) {
self.shimmerView = shimmerView
self.placeholderView = PortalView()
self.placeholderMaskLayer = SimpleLayer()
super.init(frame: CGRect())
if let placeholderView = self.placeholderView, let shimmerView = self.shimmerView {
placeholderView.view.clipsToBounds = true
placeholderView.view.layer.mask = self.placeholderMaskLayer
self.addSubview(placeholderView.view)
shimmerView.addPortal(view: placeholderView)
}
Queue.concurrentDefaultQueue().async { [weak self] in
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: size, scale: min(2.0, UIScreenScale), imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: color ?? .black) {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if let _ = color {
strongSelf.layer.contents = image.cgImage
} else {
strongSelf.placeholderMaskLayer.contents = image.cgImage
}
}
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(size: CGSize) {
if let placeholderView = self.placeholderView {
placeholderView.view.frame = CGRect(origin: CGPoint(), size: size)
}
self.placeholderMaskLayer.frame = CGRect(origin: CGPoint(), size: size)
}
}
final class ItemLayer: MultiAnimationRenderTarget {
struct Key: Hashable {
var groupId: AnyHashable
@ -353,7 +414,8 @@ public final class EmojiPagerContentComponent: Component {
}
}
}
private var displayPlaceholder: Bool = false
private(set) var displayPlaceholder: Bool = false
let onUpdateDisplayPlaceholder: (Bool) -> Void
init(
item: Item,
@ -366,11 +428,13 @@ public final class EmojiPagerContentComponent: Component {
placeholderColor: UIColor,
blurredBadgeColor: UIColor,
displayPremiumBadgeIfAvailable: Bool,
pointSize: CGSize
pointSize: CGSize,
onUpdateDisplayPlaceholder: @escaping (Bool) -> Void
) {
self.item = item
self.file = file
self.placeholderColor = placeholderColor
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
let scale = min(2.0, UIScreenScale)
let pixelSize = CGSize(width: pointSize.width * scale, height: pointSize.height * scale)
@ -414,17 +478,21 @@ public final class EmojiPagerContentComponent: Component {
if attemptSynchronousLoad {
if !renderer.loadFirstFrameSynchronously(groupId: groupId, target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize) {
self.displayPlaceholder = true
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: self.size, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor) {
self.contents = image.cgImage
}
self.updateDisplayPlaceholder(displayPlaceholder: true)
}
loadAnimation()
} else {
let _ = renderer.loadFirstFrame(groupId: groupId, target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, completion: { _ in
let _ = renderer.loadFirstFrame(groupId: groupId, target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, completion: { [weak self] success in
loadAnimation()
if !success {
guard let strongSelf = self else {
return
}
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
}
})
}
} else if let dimensions = file.dimensions {
@ -458,9 +526,21 @@ public final class EmojiPagerContentComponent: Component {
}
override public init(layer: Any) {
guard let layer = layer as? ItemLayer else {
preconditionFailure()
}
self.item = layer.item
self.file = layer.file
self.placeholderColor = layer.placeholderColor
self.size = layer.size
self.onUpdateDisplayPlaceholder = { _ in }
super.init(layer: layer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@ -492,34 +572,65 @@ public final class EmojiPagerContentComponent: Component {
}
self.displayPlaceholder = displayPlaceholder
self.onUpdateDisplayPlaceholder(displayPlaceholder)
/*if displayPlaceholder {
if self.placeholderView == nil {
self.placeholderView = PortalView()
if let placeholderView = self.placeholderView, let shimmerView = self.shimmerView {
self.addSublayer(placeholderView.view.layer)
placeholderView.view.frame = self.bounds
shimmerView.addPortal(view: placeholderView)
}
}
if self.placeholderMaskLayer == nil {
self.placeholderMaskLayer = SimpleLayer()
self.placeholderView?.view.layer.mask = self.placeholderMaskLayer
}
let file = self.file
let size = self.size
let placeholderColor = self.placeholderColor
//let placeholderColor = self.placeholderColor
Queue.concurrentDefaultQueue().async { [weak self] in
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: size, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor) {
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: size, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: .black) {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if strongSelf.displayPlaceholder {
strongSelf.contents = image.cgImage
strongSelf.placeholderMaskLayer?.contents = image.cgImage
}
}
}
}
} else {
if let placeholderView = self.placeholderView {
self.placeholderView = nil
placeholderView.view.layer.removeFromSuperlayer()
}
if let _ = self.placeholderMaskLayer {
self.placeholderMaskLayer = nil
}
}*/
}
}
private final class ContentScrollView: UIScrollView, PagerExpandableScrollView {
}
private let scrollView: ContentScrollView
private let shimmerHostView: PortalSourceView
private let standaloneShimmerEffect: StandaloneShimmerEffect
private let scrollView: ContentScrollView
private let boundsChangeTrackerLayer = SimpleLayer()
private var effectiveVisibleSize: CGSize = CGSize()
private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:]
private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:]
private var visibleGroupHeaders: [AnyHashable: ComponentView<Empty>] = [:]
private var ignoreScrolling: Bool = false
private var keepTopPanelVisibleUntilScrollingInput: Bool = false
private var component: EmojiPagerContentComponent?
private var pagerEnvironment: PagerComponentChildEnvironment?
@ -532,10 +643,24 @@ public final class EmojiPagerContentComponent: Component {
private weak var peekController: PeekController?
override init(frame: CGRect) {
self.shimmerHostView = PortalSourceView()
self.standaloneShimmerEffect = StandaloneShimmerEffect()
self.scrollView = ContentScrollView()
self.scrollView.layer.anchorPoint = CGPoint()
super.init(frame: frame)
self.shimmerHostView.alpha = 0.0
self.addSubview(self.shimmerHostView)
self.boundsChangeTrackerLayer.opacity = 0.0
self.layer.addSublayer(self.boundsChangeTrackerLayer)
self.boundsChangeTrackerLayer.didEnterHierarchy = { [weak self] in
self?.standaloneShimmerEffect.updateLayer()
}
self.scrollView.delaysContentTouches = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollView.contentInsetAdjustmentBehavior = .never
@ -546,8 +671,11 @@ public final class EmojiPagerContentComponent: Component {
self.scrollView.showsVerticalScrollIndicator = true
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.delegate = self
self.scrollView.clipsToBounds = false
self.addSubview(self.scrollView)
//self.clipsToBounds = true
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
/*self.useSublayerTransformForActivation = false
@ -747,7 +875,9 @@ public final class EmojiPagerContentComponent: Component {
self.scrollView.setContentOffset(self.scrollView.contentOffset, animated: false)
self.ignoreScrolling = wasIgnoringScrollingEvents
self.scrollView.scrollRectToVisible(CGRect(origin: group.frame.origin.offsetBy(dx: 0.0, dy: floor(-itemLayout.verticalGroupSpacing / 2.0)), size: CGSize(width: 1.0, height: self.scrollView.bounds.height)), animated: true)
self.keepTopPanelVisibleUntilScrollingInput = true
self.scrollView.scrollRectToVisible(CGRect(origin: group.frame.origin.offsetBy(dx: 0.0, dy: floor(-itemLayout.verticalGroupSpacing / 2.0) - 41.0), size: CGSize(width: 1.0, height: self.scrollView.bounds.height)), animated: true)
}
}
}
@ -780,6 +910,11 @@ public final class EmojiPagerContentComponent: Component {
private var previousScrollingOffset: ScrollingOffsetState?
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if self.keepTopPanelVisibleUntilScrollingInput {
self.keepTopPanelVisibleUntilScrollingInput = false
self.updateScrollingOffset(isReset: true, transition: .immediate)
}
if let presentation = scrollView.layer.presentation() {
scrollView.bounds = presentation.bounds
scrollView.layer.removeAllAnimations()
@ -793,7 +928,7 @@ public final class EmojiPagerContentComponent: Component {
self.updateVisibleItems(attemptSynchronousLoads: false)
self.updateScrollingOffset(transition: .immediate)
self.updateScrollingOffset(isReset: false, transition: .immediate)
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
@ -812,22 +947,29 @@ public final class EmojiPagerContentComponent: Component {
self.snapScrollingOffsetToInsets()
}
private func updateScrollingOffset(transition: Transition) {
private func updateScrollingOffset(isReset: Bool, transition: Transition) {
guard let component = self.component else {
return
}
let isInteracting = scrollView.isDragging || scrollView.isDecelerating
if let previousScrollingOffsetValue = self.previousScrollingOffset {
if let previousScrollingOffsetValue = self.previousScrollingOffset, !self.keepTopPanelVisibleUntilScrollingInput {
let currentBounds = scrollView.bounds
let offsetToTopEdge = max(0.0, currentBounds.minY - 0.0)
let offsetToBottomEdge = max(0.0, scrollView.contentSize.height - currentBounds.maxY)
let relativeOffset = scrollView.contentOffset.y - previousScrollingOffsetValue.value
if case .detailed = component.itemLayoutType {
self.pagerEnvironment?.onChildScrollingUpdate(PagerComponentChildEnvironment.ContentScrollingUpdate(
relativeOffset: relativeOffset,
absoluteOffsetToTopEdge: offsetToTopEdge,
absoluteOffsetToBottomEdge: offsetToBottomEdge,
isReset: isReset,
isInteracting: isInteracting,
transition: transition
))
}
}
self.previousScrollingOffset = ScrollingOffsetState(value: scrollView.contentOffset.y, isDraggingOrDecelerating: isInteracting)
}
@ -855,7 +997,7 @@ public final class EmojiPagerContentComponent: Component {
currentBounds.origin.y = self.snappedContentOffset(proposedOffset: currentBounds.minY)
transition.setBounds(view: self.scrollView, bounds: currentBounds)
self.updateScrollingOffset(transition: transition)
self.updateScrollingOffset(isReset: false, transition: transition)
}
private func updateVisibleItems(attemptSynchronousLoads: Bool) {
@ -868,14 +1010,17 @@ public final class EmojiPagerContentComponent: Component {
var validIds = Set<ItemLayer.Key>()
var validGroupHeaderIds = Set<AnyHashable>()
for groupItems in itemLayout.visibleItems(for: self.scrollView.bounds) {
if topVisibleGroupId == nil {
topVisibleGroupId = groupItems.id
}
let effectiveVisibleBounds = CGRect(origin: self.scrollView.bounds.origin, size: self.effectiveVisibleSize)
let topVisibleDetectionBounds = effectiveVisibleBounds.offsetBy(dx: 0.0, dy: 41.0)
for groupItems in itemLayout.visibleItems(for: effectiveVisibleBounds) {
let itemGroup = component.itemGroups[groupItems.groupIndex]
let itemGroupLayout = itemLayout.itemGroupLayouts[groupItems.groupIndex]
if topVisibleGroupId == nil && itemGroupLayout.frame.intersects(topVisibleDetectionBounds) {
topVisibleGroupId = groupItems.id
}
if let title = itemGroup.title {
validGroupHeaderIds.insert(itemGroup.id)
let groupHeaderView: ComponentView<Empty>
@ -888,7 +1033,7 @@ public final class EmojiPagerContentComponent: Component {
let groupHeaderSize = groupHeaderView.update(
transition: .immediate,
component: AnyComponent(Text(
text: title, font: Font.medium(12.0), color: theme.chat.inputMediaPanel.stickersSectionTextColor
text: title.uppercased(), font: Font.medium(12.0), color: theme.chat.inputMediaPanel.stickersSectionTextColor
)),
environment: {},
containerSize: CGSize(width: itemLayout.contentSize.width - itemLayout.containerInsets.left - itemLayout.containerInsets.right, height: 100.0)
@ -906,14 +1051,21 @@ public final class EmojiPagerContentComponent: Component {
let itemId = ItemLayer.Key(groupId: itemGroup.id, fileId: item.file.fileId)
validIds.insert(itemId)
let itemDimensions = item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
let itemNativeFitSize = itemDimensions.fitted(CGSize(width: itemLayout.nativeItemSize, height: itemLayout.nativeItemSize))
let itemVisibleFitSize = itemDimensions.fitted(CGSize(width: itemLayout.visibleItemSize, height: itemLayout.visibleItemSize))
var updateItemLayerPlaceholder = false
let itemLayer: ItemLayer
if let current = self.visibleItemLayers[itemId] {
itemLayer = current
} else {
updateItemLayerPlaceholder = true
itemLayer = ItemLayer(
item: item,
context: component.context,
groupId: "keyboard-\(Int(itemLayout.itemSize))",
groupId: "keyboard-\(Int(itemLayout.nativeItemSize))",
attemptSynchronousLoad: attemptSynchronousLoads,
file: item.file,
cache: component.animationCache,
@ -921,15 +1073,72 @@ public final class EmojiPagerContentComponent: Component {
placeholderColor: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.1),
blurredBadgeColor: theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(0.5),
displayPremiumBadgeIfAvailable: true,
pointSize: CGSize(width: itemLayout.itemSize, height: itemLayout.itemSize)
pointSize: itemNativeFitSize,
onUpdateDisplayPlaceholder: { [weak self] displayPlaceholder in
guard let strongSelf = self else {
return
}
if displayPlaceholder {
if let itemLayer = strongSelf.visibleItemLayers[itemId] {
let placeholderView: ItemPlaceholderView
if let current = strongSelf.visibleItemPlaceholderViews[itemId] {
placeholderView = current
} else {
placeholderView = ItemPlaceholderView(
context: component.context,
file: item.file,
shimmerView: strongSelf.shimmerHostView,
color: nil,
size: itemNativeFitSize
)
strongSelf.visibleItemPlaceholderViews[itemId] = placeholderView
strongSelf.scrollView.insertSubview(placeholderView, at: 0)
}
placeholderView.frame = itemLayer.frame
placeholderView.update(size: placeholderView.bounds.size)
strongSelf.updateShimmerIfNeeded()
}
} else {
if let placeholderView = strongSelf.visibleItemPlaceholderViews[itemId] {
strongSelf.visibleItemPlaceholderViews.removeValue(forKey: itemId)
placeholderView.removeFromSuperview()
strongSelf.updateShimmerIfNeeded()
}
}
}
)
self.scrollView.layer.addSublayer(itemLayer)
self.visibleItemLayers[itemId] = itemLayer
}
let itemFrame = itemLayout.frame(groupIndex: groupItems.groupIndex, itemIndex: index)
itemLayer.position = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
var itemFrame = itemLayout.frame(groupIndex: groupItems.groupIndex, itemIndex: index)
itemFrame.origin.x += floor((itemFrame.width - itemVisibleFitSize.width) / 2.0)
itemFrame.origin.y += floor((itemFrame.height - itemVisibleFitSize.height) / 2.0)
itemFrame.size = itemVisibleFitSize
let itemPosition = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
let itemBounds = CGRect(origin: CGPoint(), size: itemFrame.size)
if itemLayer.position != itemPosition {
itemLayer.position = itemPosition
}
if itemLayer.bounds != itemBounds {
itemLayer.bounds = CGRect(origin: CGPoint(), size: itemFrame.size)
}
if let placeholderView = self.visibleItemPlaceholderViews[itemId] {
if placeholderView.layer.position != itemPosition || placeholderView.layer.bounds != itemBounds {
placeholderView.frame = itemFrame
placeholderView.update(size: itemFrame.size)
}
} else if updateItemLayerPlaceholder {
if itemLayer.displayPlaceholder {
itemLayer.onUpdateDisplayPlaceholder(true)
}
}
itemLayer.isVisibleForAnimations = true
}
}
@ -943,6 +1152,10 @@ public final class EmojiPagerContentComponent: Component {
}
for id in removedIds {
self.visibleItemLayers.removeValue(forKey: id)
if let view = self.visibleItemPlaceholderViews.removeValue(forKey: id) {
view.removeFromSuperview()
}
}
var removedGroupHeaderIds: [AnyHashable] = []
@ -961,14 +1174,31 @@ public final class EmojiPagerContentComponent: Component {
}
}
private func updateShimmerIfNeeded() {
if self.visibleItemPlaceholderViews.isEmpty {
self.standaloneShimmerEffect.layer = nil
} else {
self.standaloneShimmerEffect.layer = self.shimmerHostView.layer
}
}
func update(component: EmojiPagerContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.component = component
self.theme = environment[EntityKeyboardChildEnvironment.self].value.theme
self.activeItemUpdated = environment[EntityKeyboardChildEnvironment.self].value.getContentActiveItemUpdated(component.id)
let keyboardChildEnvironment = environment[EntityKeyboardChildEnvironment.self].value
self.theme = keyboardChildEnvironment.theme
self.activeItemUpdated = keyboardChildEnvironment.getContentActiveItemUpdated(component.id)
let pagerEnvironment = environment[PagerComponentChildEnvironment.self].value
self.pagerEnvironment = pagerEnvironment
transition.setFrame(view: self.shimmerHostView, frame: CGRect(origin: CGPoint(), size: availableSize))
let shimmerBackgroundColor = keyboardChildEnvironment.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08)
let shimmerForegroundColor = keyboardChildEnvironment.theme.list.itemBlocksBackgroundColor.withMultipliedAlpha(0.15)
self.standaloneShimmerEffect.update(background: shimmerBackgroundColor, foreground: shimmerForegroundColor)
var itemGroups: [ItemGroupDescription] = []
for itemGroup in component.itemGroups {
itemGroups.append(ItemGroupDescription(
@ -982,7 +1212,28 @@ public final class EmojiPagerContentComponent: Component {
self.itemLayout = itemLayout
self.ignoreScrolling = true
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize))
transition.setPosition(view: self.scrollView, position: CGPoint())
let previousSize = self.scrollView.bounds.size
self.scrollView.bounds = CGRect(origin: self.scrollView.bounds.origin, size: availableSize)
if availableSize.height > previousSize.height || transition.animation.isImmediate {
self.boundsChangeTrackerLayer.removeAllAnimations()
self.boundsChangeTrackerLayer.bounds = self.scrollView.bounds
self.effectiveVisibleSize = self.scrollView.bounds.size
} else {
self.effectiveVisibleSize = CGSize(width: availableSize.width, height: max(self.effectiveVisibleSize.height, availableSize.height))
transition.setBounds(layer: self.boundsChangeTrackerLayer, bounds: self.scrollView.bounds, completion: { [weak self] completed in
guard let strongSelf = self else {
return
}
let effectiveVisibleSize = strongSelf.scrollView.bounds.size
if strongSelf.effectiveVisibleSize != effectiveVisibleSize {
strongSelf.effectiveVisibleSize = effectiveVisibleSize
strongSelf.updateVisibleItems(attemptSynchronousLoads: false)
}
})
}
if self.scrollView.contentSize != itemLayout.contentSize {
self.scrollView.contentSize = itemLayout.contentSize
}
@ -992,7 +1243,7 @@ public final class EmojiPagerContentComponent: Component {
self.previousScrollingOffset = ScrollingOffsetState(value: scrollView.contentOffset.y, isDraggingOrDecelerating: scrollView.isDragging || scrollView.isDecelerating)
self.ignoreScrolling = false
self.updateVisibleItems(attemptSynchronousLoads: true)
self.updateVisibleItems(attemptSynchronousLoads: !(scrollView.isDragging || scrollView.isDecelerating))
return availableSize
}

View File

@ -47,11 +47,14 @@ public final class EntityKeyboardComponent: Component {
public let stickerContent: EmojiPagerContentComponent
public let gifContent: GifPagerContentComponent
public let defaultToEmojiTab: Bool
public let externalTopPanelContainer: UIView?
public let externalTopPanelContainer: PagerExternalTopPanelContainer?
public let topPanelExtensionUpdated: (CGFloat, Transition) -> Void
public let hideInputUpdated: (Bool, Transition) -> Void
public let hideInputUpdated: (Bool, Bool, Transition) -> Void
public let switchToTextInput: () -> Void
public let makeSearchContainerNode: (EntitySearchContentType) -> EntitySearchContainerNode
public let deviceMetrics: DeviceMetrics
public let hiddenInputHeight: CGFloat
public let isExpanded: Bool
public init(
theme: PresentationTheme,
@ -60,11 +63,14 @@ public final class EntityKeyboardComponent: Component {
stickerContent: EmojiPagerContentComponent,
gifContent: GifPagerContentComponent,
defaultToEmojiTab: Bool,
externalTopPanelContainer: UIView?,
externalTopPanelContainer: PagerExternalTopPanelContainer?,
topPanelExtensionUpdated: @escaping (CGFloat, Transition) -> Void,
hideInputUpdated: @escaping (Bool, Transition) -> Void,
hideInputUpdated: @escaping (Bool, Bool, Transition) -> Void,
switchToTextInput: @escaping () -> Void,
makeSearchContainerNode: @escaping (EntitySearchContentType) -> EntitySearchContainerNode,
deviceMetrics: DeviceMetrics
deviceMetrics: DeviceMetrics,
hiddenInputHeight: CGFloat,
isExpanded: Bool
) {
self.theme = theme
self.bottomInset = bottomInset
@ -75,8 +81,11 @@ public final class EntityKeyboardComponent: Component {
self.externalTopPanelContainer = externalTopPanelContainer
self.topPanelExtensionUpdated = topPanelExtensionUpdated
self.hideInputUpdated = hideInputUpdated
self.switchToTextInput = switchToTextInput
self.makeSearchContainerNode = makeSearchContainerNode
self.deviceMetrics = deviceMetrics
self.hiddenInputHeight = hiddenInputHeight
self.isExpanded = isExpanded
}
public static func ==(lhs: EntityKeyboardComponent, rhs: EntityKeyboardComponent) -> Bool {
@ -104,6 +113,12 @@ public final class EntityKeyboardComponent: Component {
if lhs.deviceMetrics != rhs.deviceMetrics {
return false
}
if lhs.hiddenInputHeight != rhs.hiddenInputHeight {
return false
}
if lhs.isExpanded != rhs.isExpanded {
return false
}
return true
}
@ -118,13 +133,14 @@ public final class EntityKeyboardComponent: Component {
private var searchComponent: EntitySearchContentComponent?
private var topPanelExtension: CGFloat?
private var isTopPanelExpanded: Bool = false
override init(frame: CGRect) {
self.pagerView = ComponentHostView<EntityKeyboardChildEnvironment>()
super.init(frame: frame)
self.clipsToBounds = true
//self.clipsToBounds = true
self.disablesInteractiveTransitionGestureRecognizer = true
self.addSubview(self.pagerView)
@ -146,7 +162,8 @@ public final class EntityKeyboardComponent: Component {
let gifsContentItemIdUpdated = ActionSlot<(AnyHashable, Transition)>()
contents.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(component.gifContent)))
var topGifItems: [EntityKeyboardTopPanelComponent.Item] = []
topGifItems.append(EntityKeyboardTopPanelComponent.Item(
topGifItems.removeAll()
/*topGifItems.append(EntityKeyboardTopPanelComponent.Item(
id: "recent",
content: AnyComponent(BundleIconComponent(
name: "Chat/Input/Media/RecentTabIcon",
@ -161,7 +178,7 @@ public final class EntityKeyboardComponent: Component {
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
maxSize: CGSize(width: 30.0, height: 30.0))
)
))
))*/
contentTopPanels.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardTopPanelComponent(
theme: component.theme,
items: topGifItems,
@ -199,20 +216,21 @@ public final class EntityKeyboardComponent: Component {
"recent": "Chat/Input/Media/RecentTabIcon",
"premium": "Chat/Input/Media/PremiumIcon"
]
if let iconName = iconMapping[id] {
let titleMapping: [String: String] = [
"recent": "Recent",
"premium": "Premium"
]
if let iconName = iconMapping[id], let title = titleMapping[id] {
topStickerItems.append(EntityKeyboardTopPanelComponent.Item(
id: itemGroup.id,
content: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: iconName,
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
maxSize: CGSize(width: 30.0, height: 30.0)
)),
action: { [weak self] in
content: AnyComponent(EntityKeyboardIconTopPanelComponent(
imageName: iconName,
theme: component.theme,
title: title,
pressed: { [weak self] in
self?.scrollToItemGroup(contentId: "stickers", groupId: itemGroup.id)
}
).minSize(CGSize(width: 30.0, height: 30.0))
)
))
))
}
} else {
@ -224,6 +242,8 @@ public final class EntityKeyboardComponent: Component {
file: itemGroup.items[0].file,
animationCache: component.stickerContent.animationCache,
animationRenderer: component.stickerContent.animationRenderer,
theme: component.theme,
title: itemGroup.title ?? "",
pressed: { [weak self] in
self?.scrollToItemGroup(contentId: "stickers", groupId: itemGroup.id)
}
@ -277,6 +297,8 @@ public final class EntityKeyboardComponent: Component {
file: itemGroup.items[0].file,
animationCache: component.emojiContent.animationCache,
animationRenderer: component.emojiContent.animationRenderer,
theme: component.theme,
title: itemGroup.title ?? "",
pressed: { [weak self] in
self?.scrollToItemGroup(contentId: "emoji", groupId: itemGroup.id)
}
@ -294,6 +316,20 @@ public final class EntityKeyboardComponent: Component {
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
maxSize: nil
))))
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: "Chat/Input/Media/EntityInputGlobeIcon",
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
maxSize: nil
)),
action: { [weak self] in
guard let strongSelf = self, let component = strongSelf.component else {
return
}
component.switchToTextInput()
}
).minSize(CGSize(width: 38.0, height: 38.0)))))
let deleteBackwards = component.emojiContent.inputInteraction.deleteBackwards
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: "Chat/Input/Media/EntityInputClearIcon",
@ -301,9 +337,20 @@ public final class EntityKeyboardComponent: Component {
maxSize: nil
)),
action: {
component.emojiContent.inputInteraction.deleteBackwards()
deleteBackwards()
}
).withHoldAction({
deleteBackwards()
}).minSize(CGSize(width: 38.0, height: 38.0)))))
let panelHideBehavior: PagerComponentPanelHideBehavior
if self.searchComponent != nil {
panelHideBehavior = .hide
} else if component.isExpanded {
panelHideBehavior = .show
} else {
panelHideBehavior = .hideOnScroll
}
).minSize(CGSize(width: 38.0, height: 38.0)))))
let pagerSize = self.pagerView.update(
transition: transition,
@ -319,7 +366,8 @@ public final class EntityKeyboardComponent: Component {
color: component.theme.chat.inputMediaPanel.stickersBackgroundColor.withMultipliedAlpha(0.75)
)),
topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent(
theme: component.theme
theme: component.theme,
overflowHeight: component.hiddenInputHeight
)),
externalTopPanelContainer: component.externalTopPanelContainer,
bottomPanel: AnyComponent(EntityKeyboardBottomPanelComponent(
@ -335,7 +383,13 @@ public final class EntityKeyboardComponent: Component {
}
strongSelf.topPanelExtensionUpdated(height: panelState.topPanelHeight, transition: transition)
},
hidePanels: self.searchComponent != nil
isTopPanelExpandedUpdated: { [weak self] isExpanded, transition in
guard let strongSelf = self else {
return
}
strongSelf.isTopPanelExpandedUpdated(isExpanded: isExpanded, transition: transition)
},
panelHideBehavior: panelHideBehavior
)),
environment: {
EntityKeyboardChildEnvironment(
@ -426,6 +480,18 @@ public final class EntityKeyboardComponent: Component {
}
}
private func isTopPanelExpandedUpdated(isExpanded: Bool, transition: Transition) {
if self.isTopPanelExpanded != isExpanded {
self.isTopPanelExpanded = isExpanded
}
guard let component = self.component else {
return
}
component.hideInputUpdated(self.isTopPanelExpanded, false, transition)
}
private func openSearch() {
guard let component = self.component else {
return
@ -451,7 +517,7 @@ public final class EntityKeyboardComponent: Component {
}
)
//self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
component.hideInputUpdated(true, Transition(animation: .curve(duration: 0.3, curve: .spring)))
component.hideInputUpdated(true, true, Transition(animation: .curve(duration: 0.3, curve: .spring)))
}
}
@ -464,7 +530,7 @@ public final class EntityKeyboardComponent: Component {
}
self.searchComponent = nil
//self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
component.hideInputUpdated(false, Transition(animation: .curve(duration: 0.4, curve: .spring)))
component.hideInputUpdated(false, false, Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
private func scrollToItemGroup(contentId: String, groupId: AnyHashable) {

View File

@ -9,11 +9,14 @@ import Postbox
final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
let visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
let isExpandedUpdated: (Bool, Transition) -> Void
init(
visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>,
isExpandedUpdated: @escaping (Bool, Transition) -> Void
) {
self.visibilityFractionUpdated = visibilityFractionUpdated
self.isExpandedUpdated = isExpandedUpdated
}
static func ==(lhs: EntityKeyboardTopContainerPanelEnvironment, rhs: EntityKeyboardTopContainerPanelEnvironment) -> Bool {
@ -28,17 +31,23 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
typealias EnvironmentType = PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>
let theme: PresentationTheme
let overflowHeight: CGFloat
init(
theme: PresentationTheme
theme: PresentationTheme,
overflowHeight: CGFloat
) {
self.theme = theme
self.overflowHeight = overflowHeight
}
static func ==(lhs: EntityKeyboardTopContainerPanelComponent, rhs: EntityKeyboardTopContainerPanelComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.overflowHeight != rhs.overflowHeight {
return false
}
return true
}
@ -46,6 +55,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
private final class PanelView {
let view = ComponentHostView<EntityKeyboardTopContainerPanelEnvironment>()
let visibilityFractionUpdated = ActionSlot<(CGFloat, Transition)>()
var isExpanded: Bool = false
}
final class View: UIView {
@ -57,8 +67,13 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
private var visibilityFraction: CGFloat = 1.0
private var hasExpandedPanels: Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
self.disablesInteractiveKeyboardGestureRecognizer = true
self.disablesInteractiveTransitionGestureRecognizer = true
}
required init?(coder: NSCoder) {
@ -94,7 +109,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
let panel = panelEnvironment.contentTopPanels[index]
let indexOffset = index - centralIndex
let panelFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * availableSize.width, y: 0.0), size: CGSize(width: availableSize.width, height: intrinsicHeight))
let panelFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * availableSize.width, y: -component.overflowHeight), size: CGSize(width: availableSize.width, height: intrinsicHeight + component.overflowHeight))
let isInBounds = visibleBounds.intersects(panelFrame)
let isPartOfTransition: Bool
@ -118,12 +133,19 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
self.addSubview(panelView.view)
}
let panelId = panel.id
let _ = panelView.view.update(
transition: panelTransition,
component: panel.component,
environment: {
EntityKeyboardTopContainerPanelEnvironment(
visibilityFractionUpdated: panelView.visibilityFractionUpdated
visibilityFractionUpdated: panelView.visibilityFractionUpdated,
isExpandedUpdated: { [weak self] isExpanded, transition in
guard let strongSelf = self else {
return
}
strongSelf.panelIsExpandedUpdated(id: panelId, isExpanded: isExpanded, transition: transition)
}
)
},
containerSize: panelFrame.size
@ -171,6 +193,45 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
transition.setSublayerTransform(view: panelView.view, transform: CATransform3DMakeTranslation(0.0, -panelView.view.bounds.height / 2.0 * (1.0 - value), 0.0))
}
}
private func panelIsExpandedUpdated(id: AnyHashable, isExpanded: Bool, transition: Transition) {
guard let panelView = self.panelViews[id] else {
return
}
panelView.isExpanded = isExpanded
var hasExpanded = false
for (_, panel) in self.panelViews {
if panel.isExpanded {
hasExpanded = true
break
}
}
if self.hasExpandedPanels != hasExpanded {
self.hasExpandedPanels = hasExpanded
self.panelEnvironment?.isExpandedUpdated(self.hasExpandedPanels, transition)
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.alpha.isZero {
return nil
}
for view in self.subviews.reversed() {
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result
}
}
let result = super.hitTest(point, with: event)
if result != self {
return result
} else {
return nil
}
}
}
func makeView() -> View {

View File

@ -1,4 +1,5 @@
import Foundation
import SwiftSignalKit
import UIKit
import Display
import ComponentFlow
@ -9,14 +10,17 @@ import Postbox
import AnimationCache
import MultiAnimationRenderer
import AccountContext
import MultilineTextComponent
final class EntityKeyboardAnimationTopPanelComponent: Component {
typealias EnvironmentType = Empty
typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment
let context: AccountContext
let file: TelegramMediaFile
let animationCache: AnimationCache
let animationRenderer: MultiAnimationRenderer
let theme: PresentationTheme
let title: String
let pressed: () -> Void
init(
@ -24,12 +28,16 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
file: TelegramMediaFile,
animationCache: AnimationCache,
animationRenderer: MultiAnimationRenderer,
theme: PresentationTheme,
title: String,
pressed: @escaping () -> Void
) {
self.context = context
self.file = file
self.animationCache = animationCache
self.animationRenderer = animationRenderer
self.theme = theme
self.title = title
self.pressed = pressed
}
@ -46,13 +54,21 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
if lhs.animationRenderer !== rhs.animationRenderer {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.title != rhs.title {
return false
}
return true
}
final class View: UIView {
var itemLayer: EmojiPagerContentComponent.View.ItemLayer?
var placeholderView: EmojiPagerContentComponent.View.ItemPlaceholderView?
var component: EntityKeyboardAnimationTopPanelComponent?
var titleView: ComponentView<Empty>?
override init(frame: CGRect) {
super.init(frame: frame)
@ -73,6 +89,8 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
func update(component: EntityKeyboardAnimationTopPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.component = component
let itemEnvironment = environment[EntityKeyboardTopPanelItemEnvironment.self].value
if self.itemLayer == nil {
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
item: EmojiPagerContentComponent.Item(
@ -89,15 +107,88 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
placeholderColor: .lightGray,
blurredBadgeColor: .clear,
displayPremiumBadgeIfAvailable: false,
pointSize: CGSize(width: 28.0, height: 28.0)
pointSize: CGSize(width: 44.0, height: 44.0),
onUpdateDisplayPlaceholder: { [weak self] displayPlaceholder in
guard let strongSelf = self else {
return
}
strongSelf.updateDisplayPlaceholder(displayPlaceholder: displayPlaceholder)
}
)
self.itemLayer = itemLayer
self.layer.addSublayer(itemLayer)
itemLayer.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0))
if itemLayer.displayPlaceholder {
self.updateDisplayPlaceholder(displayPlaceholder: true)
}
}
let iconSize: CGSize = itemEnvironment.isExpanded ? CGSize(width: 44.0, height: 44.0) : CGSize(width: 28.0, height: 28.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) / 2.0), y: 0.0), size: iconSize)
if let itemLayer = self.itemLayer {
transition.setPosition(layer: itemLayer, position: CGPoint(x: iconFrame.midX, y: iconFrame.midY))
transition.setBounds(layer: itemLayer, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
itemLayer.isVisibleForAnimations = true
}
return CGSize(width: 28.0, height: 28.0)
if itemEnvironment.isExpanded {
let titleView: ComponentView<Empty>
if let current = self.titleView {
titleView = current
} else {
titleView = ComponentView<Empty>()
self.titleView = titleView
}
let titleSize = titleView.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.title, font: Font.regular(10.0), textColor: component.theme.chat.inputPanel.primaryTextColor))
)),
environment: {},
containerSize: CGSize(width: 62.0, height: 100.0)
)
if let view = titleView.view {
if view.superview == nil {
view.alpha = 0.0
self.addSubview(view)
}
view.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: availableSize.height - titleSize.height), size: titleSize)
transition.setAlpha(view: view, alpha: 1.0)
}
} else if let titleView = self.titleView {
self.titleView = nil
if let view = titleView.view {
transition.setAlpha(view: view, alpha: 0.0, completion: { [weak view] _ in
view?.removeFromSuperview()
})
}
}
return availableSize
}
private func updateDisplayPlaceholder(displayPlaceholder: Bool) {
if displayPlaceholder {
if self.placeholderView == nil, let component = self.component {
let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
context: component.context,
file: component.file,
shimmerView: nil,
color: component.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
size: CGSize(width: 28.0, height: 28.0)
)
self.placeholderView = placeholderView
self.insertSubview(placeholderView, at: 0)
placeholderView.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0))
placeholderView.update(size: CGSize(width: 28.0, height: 28.0))
}
} else {
if let placeholderView = self.placeholderView {
self.placeholderView = nil
placeholderView.removeFromSuperview()
}
}
}
}
@ -110,14 +201,151 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
}
}
final class EntityKeyboardIconTopPanelComponent: Component {
typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment
let imageName: String
let theme: PresentationTheme
let title: String
let pressed: () -> Void
init(
imageName: String,
theme: PresentationTheme,
title: String,
pressed: @escaping () -> Void
) {
self.imageName = imageName
self.theme = theme
self.title = title
self.pressed = pressed
}
static func ==(lhs: EntityKeyboardIconTopPanelComponent, rhs: EntityKeyboardIconTopPanelComponent) -> Bool {
if lhs.imageName != rhs.imageName {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.title != rhs.title {
return false
}
return true
}
final class View: UIView {
let iconView: UIImageView
var component: EntityKeyboardIconTopPanelComponent?
var titleView: ComponentView<Empty>?
override init(frame: CGRect) {
self.iconView = UIImageView()
super.init(frame: frame)
self.addSubview(self.iconView)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.component?.pressed()
}
}
func update(component: EntityKeyboardIconTopPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let itemEnvironment = environment[EntityKeyboardTopPanelItemEnvironment.self].value
if self.component?.imageName != component.imageName {
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: component.imageName), color: component.theme.chat.inputMediaPanel.panelIconColor)
}
self.component = component
let nativeIconSize: CGSize = itemEnvironment.isExpanded ? CGSize(width: 44.0, height: 44.0) : CGSize(width: 28.0, height: 28.0)
let boundingIconSize: CGSize = itemEnvironment.isExpanded ? CGSize(width: 38.0, height: 38.0) : CGSize(width: 24.0, height: 24.0)
let iconSize = (self.iconView.image?.size ?? nativeIconSize).aspectFitted(boundingIconSize)
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) / 2.0), y: floor((nativeIconSize.height - iconSize.height) / 2.0)), size: iconSize)
transition.setFrame(view: self.iconView, frame: iconFrame)
if itemEnvironment.isExpanded {
let titleView: ComponentView<Empty>
if let current = self.titleView {
titleView = current
} else {
titleView = ComponentView<Empty>()
self.titleView = titleView
}
let titleSize = titleView.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.title, font: Font.regular(10.0), textColor: component.theme.chat.inputPanel.primaryTextColor))
)),
environment: {},
containerSize: CGSize(width: 62.0, height: 100.0)
)
if let view = titleView.view {
if view.superview == nil {
view.alpha = 0.0
self.addSubview(view)
}
view.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: availableSize.height - titleSize.height), size: titleSize)
transition.setAlpha(view: view, alpha: 1.0)
}
} else if let titleView = self.titleView {
self.titleView = nil
if let view = titleView.view {
transition.setAlpha(view: view, alpha: 0.0, completion: { [weak view] _ in
view?.removeFromSuperview()
})
}
}
return availableSize
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
final class EntityKeyboardTopPanelItemEnvironment: Equatable {
let isExpanded: Bool
init(isExpanded: Bool) {
self.isExpanded = isExpanded
}
static func ==(lhs: EntityKeyboardTopPanelItemEnvironment, rhs: EntityKeyboardTopPanelItemEnvironment) -> Bool {
if lhs.isExpanded != rhs.isExpanded {
return false
}
return true
}
}
final class EntityKeyboardTopPanelComponent: Component {
typealias EnvironmentType = EntityKeyboardTopContainerPanelEnvironment
final class Item: Equatable {
let id: AnyHashable
let content: AnyComponent<Empty>
let content: AnyComponent<EntityKeyboardTopPanelItemEnvironment>
init(id: AnyHashable, content: AnyComponent<Empty>) {
init(id: AnyHashable, content: AnyComponent<EntityKeyboardTopPanelItemEnvironment>) {
self.id = id
self.content = content
}
@ -165,34 +393,42 @@ final class EntityKeyboardTopPanelComponent: Component {
final class View: UIView, UIScrollViewDelegate {
private struct ItemLayout {
let sideInset: CGFloat = 7.0
let itemSize: CGFloat = 32.0
let innerItemSize: CGFloat = 28.0
let itemSize: CGSize
let innerItemSize: CGSize
let itemSpacing: CGFloat = 15.0
let itemCount: Int
let contentSize: CGSize
let isExpanded: Bool
init(itemCount: Int) {
init(itemCount: Int, isExpanded: Bool, height: CGFloat) {
self.isExpanded = isExpanded
self.itemSize = self.isExpanded ? CGSize(width: 54.0, height: 68.0) : CGSize(width: 32.0, height: 32.0)
self.innerItemSize = self.isExpanded ? CGSize(width: 50.0, height: 62.0) : CGSize(width: 28.0, height: 28.0)
self.itemCount = itemCount
self.contentSize = CGSize(width: sideInset * 2.0 + CGFloat(itemCount) * self.itemSize + CGFloat(max(0, itemCount - 1)) * itemSpacing, height: 41.0)
self.contentSize = CGSize(width: sideInset * 2.0 + CGFloat(itemCount) * self.itemSize.width + CGFloat(max(0, itemCount - 1)) * itemSpacing, height: height)
}
func containerFrame(at index: Int) -> CGRect {
return CGRect(origin: CGPoint(x: sideInset + CGFloat(index) * (self.itemSize + self.itemSpacing), y: floor((self.contentSize.height - self.itemSize) / 2.0)), size: CGSize(width: self.itemSize, height: self.itemSize))
return CGRect(origin: CGPoint(x: sideInset + CGFloat(index) * (self.itemSize.width + self.itemSpacing), y: floor((self.contentSize.height - self.itemSize.height) / 2.0)), size: self.itemSize)
}
func contentFrame(containerFrame: CGRect) -> CGRect {
var frame = containerFrame
frame.origin.x += floor((self.itemSize.width - self.innerItemSize.width)) / 2.0
frame.origin.y += floor((self.itemSize.height - self.innerItemSize.height)) / 2.0
frame.size = self.innerItemSize
return frame
}
func contentFrame(at index: Int) -> CGRect {
var frame = self.containerFrame(at: index)
frame.origin.x += floor((self.itemSize - self.innerItemSize)) / 2.0
frame.origin.y += floor((self.itemSize - self.innerItemSize)) / 2.0
frame.size = CGSize(width: self.innerItemSize, height: self.innerItemSize)
return frame
return self.contentFrame(containerFrame: self.containerFrame(at: index))
}
func visibleItemRange(for rect: CGRect) -> (minIndex: Int, maxIndex: Int) {
let offsetRect = rect.offsetBy(dx: -self.sideInset, dy: 0.0)
var minVisibleColumn = Int(floor((offsetRect.minX - self.itemSpacing) / (self.itemSize + self.itemSpacing)))
var minVisibleColumn = Int(floor((offsetRect.minX - self.itemSpacing) / (self.itemSize.width + self.itemSpacing)))
minVisibleColumn = max(0, minVisibleColumn)
let maxVisibleColumn = Int(ceil((offsetRect.maxX - self.itemSpacing) / (self.itemSize + self.itemSpacing)))
let maxVisibleColumn = Int(ceil((offsetRect.maxX - self.itemSpacing) / (self.itemSize.width + self.itemSpacing)))
let minVisibleIndex = minVisibleColumn
let maxVisibleIndex = min(maxVisibleColumn, self.itemCount - 1)
@ -202,15 +438,23 @@ final class EntityKeyboardTopPanelComponent: Component {
}
private let scrollView: UIScrollView
private var itemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
private var itemViews: [AnyHashable: ComponentHostView<EntityKeyboardTopPanelItemEnvironment>] = [:]
private var highlightedIconBackgroundView: UIView
private var itemLayout: ItemLayout?
private var ignoreScrolling: Bool = false
private var isDragging: Bool = false
private var draggingStoppedTimer: SwiftSignalKit.Timer?
private var isExpanded: Bool = false
private var visibilityFraction: CGFloat = 1.0
private var activeContentItemId: AnyHashable?
private var component: EntityKeyboardTopPanelComponent?
private var environment: EntityKeyboardTopContainerPanelEnvironment?
override init(frame: CGRect) {
self.scrollView = UIScrollView()
@ -222,7 +466,9 @@ final class EntityKeyboardTopPanelComponent: Component {
super.init(frame: frame)
self.scrollView.layer.anchorPoint = CGPoint()
self.scrollView.delaysContentTouches = false
self.scrollView.clipsToBounds = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollView.contentInsetAdjustmentBehavior = .never
}
@ -236,6 +482,8 @@ final class EntityKeyboardTopPanelComponent: Component {
self.scrollView.addSubview(self.highlightedIconBackgroundView)
self.clipsToBounds = true
self.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
guard let strongSelf = self else {
return false
@ -253,38 +501,98 @@ final class EntityKeyboardTopPanelComponent: Component {
return
}
self.updateVisibleItems(attemptSynchronousLoads: false)
self.updateVisibleItems(attemptSynchronousLoads: false, transition: .immediate)
}
private func updateVisibleItems(attemptSynchronousLoads: Bool) {
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.updateIsDragging(true)
}
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
self.updateIsDragging(false)
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.updateIsDragging(false)
}
private func updateIsDragging(_ isDragging: Bool) {
if !isDragging {
if !self.isDragging {
return
}
if self.draggingStoppedTimer == nil {
self.draggingStoppedTimer = SwiftSignalKit.Timer(timeout: 0.8, repeat: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.draggingStoppedTimer = nil
strongSelf.isDragging = false
guard let environment = strongSelf.environment else {
return
}
environment.isExpandedUpdated(false, Transition(animation: .curve(duration: 0.3, curve: .spring)))
}, queue: .mainQueue())
self.draggingStoppedTimer?.start()
}
} else {
self.draggingStoppedTimer?.invalidate()
self.draggingStoppedTimer = nil
if !self.isDragging {
self.isDragging = true
guard let environment = self.environment else {
return
}
environment.isExpandedUpdated(true, Transition(animation: .curve(duration: 0.3, curve: .spring)))
}
}
}
private func updateVisibleItems(attemptSynchronousLoads: Bool, transition: Transition) {
guard let component = self.component, let itemLayout = self.itemLayout else {
return
}
var visibleBounds = self.scrollView.bounds
visibleBounds.size.width += 200.0
var validIds = Set<AnyHashable>()
let visibleItemRange = itemLayout.visibleItemRange(for: self.scrollView.bounds)
let visibleItemRange = itemLayout.visibleItemRange(for: visibleBounds)
if !component.items.isEmpty && visibleItemRange.maxIndex >= visibleItemRange.minIndex {
for index in visibleItemRange.minIndex ... visibleItemRange.maxIndex {
let item = component.items[index]
validIds.insert(item.id)
let itemView: ComponentHostView<Empty>
var itemTransition = transition
let itemView: ComponentHostView<EntityKeyboardTopPanelItemEnvironment>
if let current = self.itemViews[item.id] {
itemView = current
} else {
itemView = ComponentHostView<Empty>()
itemTransition = .immediate
itemView = ComponentHostView<EntityKeyboardTopPanelItemEnvironment>()
self.scrollView.addSubview(itemView)
self.itemViews[item.id] = itemView
}
let itemOuterFrame = itemLayout.contentFrame(at: index)
let itemSize = itemView.update(
transition: .immediate,
transition: itemTransition,
component: item.content,
environment: {},
environment: {
EntityKeyboardTopPanelItemEnvironment(isExpanded: itemLayout.isExpanded)
},
containerSize: itemOuterFrame.size
)
itemView.frame = 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)
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)
/*if index == visibleItemRange.minIndex, !itemTransition.animation.isImmediate {
print("\(index): \(itemView.frame) -> \(itemFrame)")
}*/
itemTransition.setFrame(view: itemView, frame: itemFrame)
}
}
var removedIds: [AnyHashable] = []
@ -305,22 +613,77 @@ final class EntityKeyboardTopPanelComponent: Component {
}
self.component = component
let intrinsicHeight: CGFloat = 41.0
let panelEnvironment = environment[EntityKeyboardTopContainerPanelEnvironment.self].value
self.environment = panelEnvironment
let isExpanded = availableSize.height > 41.0
let wasExpanded = self.isExpanded
self.isExpanded = isExpanded
let intrinsicHeight: CGFloat = availableSize.height
let height = intrinsicHeight
let itemLayout = ItemLayout(itemCount: component.items.count)
let previousItemLayout = self.itemLayout
let itemLayout = ItemLayout(itemCount: component.items.count, isExpanded: isExpanded, height: availableSize.height)
self.itemLayout = itemLayout
self.ignoreScrolling = true
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: intrinsicHeight)))
var updatedBounds: CGRect?
if wasExpanded != isExpanded, let previousItemLayout = previousItemLayout {
var visibleBounds = self.scrollView.bounds
visibleBounds.size.width += 200.0
let previousVisibleRange = previousItemLayout.visibleItemRange(for: visibleBounds)
if previousVisibleRange.minIndex <= previousVisibleRange.maxIndex {
let previousItemFrame = previousItemLayout.containerFrame(at: previousVisibleRange.minIndex)
let updatedItemFrame = itemLayout.containerFrame(at: previousVisibleRange.minIndex)
let previousDistanceToItemFraction = (previousItemFrame.minX - self.scrollView.bounds.minX) / previousItemFrame.width
let newBounds = CGRect(origin: CGPoint(x: updatedItemFrame.minX - floor(previousDistanceToItemFraction * updatedItemFrame.width), y: 0.0), size: availableSize)
updatedBounds = newBounds
var updatedVisibleBounds = newBounds
updatedVisibleBounds.size.width += 200.0
let updatedVisibleRange = itemLayout.visibleItemRange(for: updatedVisibleBounds)
let baseFrame = CGRect(origin: CGPoint(x: updatedItemFrame.minX, y: previousItemFrame.minY), size: previousItemFrame.size)
for index in updatedVisibleRange.minIndex ..< updatedVisibleRange.maxIndex {
let indexDifference = index - previousVisibleRange.minIndex
if let itemView = self.itemViews[component.items[index].id] {
let itemContainerOriginX = baseFrame.minX + CGFloat(indexDifference) * (previousItemLayout.itemSize.width + previousItemLayout.itemSpacing)
let itemContainerFrame = CGRect(origin: CGPoint(x: itemContainerOriginX, y: baseFrame.minY), size: baseFrame.size)
let itemOuterFrame = previousItemLayout.contentFrame(containerFrame: itemContainerFrame)
let itemSize = itemView.bounds.size
itemView.frame = 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)
}
}
}
}
if self.scrollView.contentSize != itemLayout.contentSize {
self.scrollView.contentSize = itemLayout.contentSize
}
if let updatedBounds = updatedBounds {
self.scrollView.bounds = updatedBounds
} else {
self.scrollView.bounds = CGRect(origin: self.scrollView.bounds.origin, size: availableSize)
}
self.ignoreScrolling = false
self.updateVisibleItems(attemptSynchronousLoads: true)
self.updateVisibleItems(attemptSynchronousLoads: !(self.scrollView.isDragging || self.scrollView.isDecelerating), transition: transition)
environment[EntityKeyboardTopContainerPanelEnvironment.self].value.visibilityFractionUpdated.connect { [weak self] (fraction, transition) in
if let activeContentItemId = self.activeContentItemId {
if let index = component.items.firstIndex(where: { $0.id == activeContentItemId }) {
let itemFrame = itemLayout.containerFrame(at: index)
transition.setPosition(view: self.highlightedIconBackgroundView, position: CGPoint(x: itemFrame.midX, y: itemFrame.midY))
transition.setBounds(view: self.highlightedIconBackgroundView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
}
}
transition.setAlpha(view: self.highlightedIconBackgroundView, alpha: isExpanded ? 0.0 : 1.0)
panelEnvironment.visibilityFractionUpdated.connect { [weak self] (fraction, transition) in
guard let strongSelf = self else {
return
}
@ -360,13 +723,18 @@ final class EntityKeyboardTopPanelComponent: Component {
return
}
self.activeContentItemId = itemId
var found = false
for i in 0 ..< component.items.count {
if component.items[i].id == itemId {
found = true
self.highlightedIconBackgroundView.isHidden = false
let itemFrame = itemLayout.containerFrame(at: i)
transition.setFrame(view: self.highlightedIconBackgroundView, frame: itemFrame)
transition.setPosition(view: self.highlightedIconBackgroundView, position: CGPoint(x: itemFrame.midX, y: itemFrame.midY))
transition.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
}

View File

@ -464,6 +464,7 @@ public final class GifPagerContentComponent: Component {
relativeOffset: relativeOffset,
absoluteOffsetToTopEdge: offsetToTopEdge,
absoluteOffsetToBottomEdge: offsetToBottomEdge,
isReset: false,
isInteracting: isInteracting,
transition: transition
))

View File

@ -3,6 +3,7 @@ import UIKit
import SwiftSignalKit
import Display
import AnimationCache
import Accelerate
public protocol MultiAnimationRenderer: AnyObject {
func add(groupId: String, target: MultiAnimationRenderTarget, cache: AnimationCache, itemId: String, size: CGSize, fetch: @escaping (CGSize, AnimationCacheItemWriter) -> Disposable) -> Disposable
@ -36,6 +37,7 @@ open class MultiAnimationRenderTarget: SimpleLayer {
private final class FrameGroup {
let image: UIImage
let badgeImage: UIImage?
let size: CGSize
let timestamp: Double
@ -50,6 +52,27 @@ private final class FrameGroup {
firstFrame.data.withUnsafeBytes { bytes -> Void in
memcpy(context.bytes, bytes.baseAddress!.advanced(by: firstFrame.range.lowerBound), height * bytesPerRow)
/*var sourceBuffer = vImage_Buffer()
sourceBuffer.width = UInt(width)
sourceBuffer.height = UInt(height)
sourceBuffer.data = UnsafeMutableRawPointer(mutating: bytes.baseAddress!.advanced(by: firstFrame.range.lowerBound))
sourceBuffer.rowBytes = bytesPerRow
var destinationBuffer = vImage_Buffer()
destinationBuffer.width = UInt(32)
destinationBuffer.height = UInt(32)
destinationBuffer.data = context.bytes
destinationBuffer.rowBytes = bytesPerRow
vImageBoxConvolve_ARGB8888(&sourceBuffer,
&destinationBuffer,
nil,
UInt(width - 32 - 16), UInt(height - 32 - 16),
UInt32(31),
UInt32(31),
nil,
vImage_Flags(kvImageEdgeExtend))*/
}
guard let image = context.generateImage() else {
@ -59,6 +82,7 @@ private final class FrameGroup {
self.image = image
self.size = CGSize(width: CGFloat(width), height: CGFloat(height))
self.timestamp = timestamp
self.badgeImage = nil
}
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "planet.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,255 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 3.500000 1.839844 cm
0.000000 0.000000 0.000000 scn
22.170000 13.160156 m
22.170000 7.267279 17.392878 2.490156 11.500000 2.490156 c
11.500000 0.830156 l
18.309671 0.830156 23.830000 6.350485 23.830000 13.160156 c
22.170000 13.160156 l
h
11.500000 2.490156 m
5.607122 2.490156 0.830000 7.267279 0.830000 13.160156 c
-0.830000 13.160156 l
-0.830000 6.350485 4.690329 0.830156 11.500000 0.830156 c
11.500000 2.490156 l
h
0.830000 13.160156 m
0.830000 19.053034 5.607122 23.830156 11.500000 23.830156 c
11.500000 25.490156 l
4.690329 25.490156 -0.830000 19.969828 -0.830000 13.160156 c
0.830000 13.160156 l
h
11.500000 23.830156 m
17.392878 23.830156 22.170000 19.053034 22.170000 13.160156 c
23.830000 13.160156 l
23.830000 19.969828 18.309671 25.490156 11.500000 25.490156 c
11.500000 23.830156 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 8.500000 1.839844 cm
0.000000 0.000000 0.000000 scn
12.170000 13.160156 m
12.170000 10.100716 11.467313 7.371819 10.373627 5.436836 c
9.265684 3.476629 7.863927 2.490156 6.500000 2.490156 c
6.500000 0.830156 l
8.725924 0.830156 10.574167 2.418047 11.818761 4.620020 c
13.077613 6.847219 13.830000 9.868322 13.830000 13.160156 c
12.170000 13.160156 l
h
6.500000 2.490156 m
5.136073 2.490156 3.734316 3.476629 2.626373 5.436836 c
1.532687 7.371819 0.830000 10.100716 0.830000 13.160156 c
-0.830000 13.160156 l
-0.830000 9.868322 -0.077613 6.847219 1.181239 4.620020 c
2.425833 2.418047 4.274076 0.830156 6.500000 0.830156 c
6.500000 2.490156 l
h
0.830000 13.160156 m
0.830000 16.219597 1.532687 18.948494 2.626373 20.883476 c
3.734316 22.843683 5.136073 23.830156 6.500000 23.830156 c
6.500000 25.490156 l
4.274076 25.490156 2.425833 23.902266 1.181239 21.700291 c
-0.077613 19.473093 -0.830000 16.451990 -0.830000 13.160156 c
0.830000 13.160156 l
h
6.500000 23.830156 m
7.863927 23.830156 9.265684 22.843683 10.373627 20.883476 c
11.467313 18.948494 12.170000 16.219597 12.170000 13.160156 c
13.830000 13.160156 l
13.830000 16.451990 13.077613 19.473093 11.818761 21.700291 c
10.574167 23.902266 8.725924 25.490156 6.500000 25.490156 c
6.500000 23.830156 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 15.000000 3.500000 cm
0.000000 0.000000 0.000000 scn
-0.830000 23.000000 m
-0.830000 0.000000 l
0.830000 0.000000 l
0.830000 23.000000 l
-0.830000 23.000000 l
h
f
n
Q
q
0.000000 1.000000 -1.000000 0.000000 5.160156 15.000000 cm
0.000000 0.000000 0.000000 scn
-0.830000 1.660156 m
-0.830000 -21.339844 l
0.830000 -21.339844 l
0.830000 1.660156 l
-0.830000 1.660156 l
h
f
n
Q
q
0.000000 1.000000 -1.000000 0.000000 9.019043 8.000000 cm
0.000000 0.000000 0.000000 scn
0.000000 3.519043 m
-0.686377 3.052367 -0.686606 3.052704 -0.686827 3.053029 c
-0.686890 3.053123 -0.687103 3.053437 -0.687230 3.053624 c
-0.687484 3.053999 -0.687707 3.054330 -0.687901 3.054617 c
-0.688287 3.055190 -0.688552 3.055586 -0.688699 3.055805 c
-0.688991 3.056243 -0.688808 3.055973 -0.688163 3.054991 c
-0.686874 3.053027 -0.683741 3.048217 -0.678884 3.040542 c
-0.669169 3.025192 -0.652556 2.998377 -0.630008 2.959931 c
-0.584915 2.883047 -0.516061 2.759615 -0.431147 2.588317 c
-0.261366 2.245815 -0.027114 1.711416 0.209833 0.974417 c
0.683340 -0.498374 1.170000 -2.787649 1.170000 -5.980957 c
2.830000 -5.980957 l
2.830000 -2.615824 2.316660 -0.155099 1.790166 1.482499 c
1.527114 2.300695 1.261366 2.911589 1.056147 3.325581 c
0.953561 3.532529 0.866165 3.690144 0.801883 3.799746 c
0.769744 3.854543 0.743388 3.897330 0.723805 3.928271 c
0.714015 3.943742 0.705917 3.956251 0.699638 3.965818 c
0.696498 3.970602 0.693813 3.974651 0.691598 3.977967 c
0.690490 3.979625 0.689500 3.981099 0.688629 3.982391 c
0.688194 3.983037 0.687788 3.983638 0.687413 3.984192 c
0.687225 3.984469 0.686966 3.984851 0.686872 3.984990 c
0.686621 3.985360 0.686377 3.985719 0.000000 3.519043 c
h
1.170000 -5.980957 m
1.170000 -9.174265 0.683340 -11.463540 0.209833 -12.936331 c
-0.027114 -13.673330 -0.261366 -14.207729 -0.431147 -14.550232 c
-0.516061 -14.721529 -0.584915 -14.844961 -0.630008 -14.921844 c
-0.652556 -14.960291 -0.669169 -14.987106 -0.678884 -15.002457 c
-0.683741 -15.010132 -0.686874 -15.014940 -0.688163 -15.016905 c
-0.688808 -15.017887 -0.688991 -15.018158 -0.688699 -15.017719 c
-0.688552 -15.017500 -0.688287 -15.017103 -0.687901 -15.016531 c
-0.687707 -15.016245 -0.687484 -15.015913 -0.687230 -15.015539 c
-0.687103 -15.015350 -0.686890 -15.015036 -0.686827 -15.014942 c
-0.686606 -15.014618 -0.686377 -15.014280 0.000000 -15.480957 c
0.686377 -15.947634 0.686621 -15.947273 0.686872 -15.946903 c
0.686966 -15.946766 0.687225 -15.946383 0.687413 -15.946106 c
0.687788 -15.945551 0.688194 -15.944952 0.688629 -15.944305 c
0.689500 -15.943014 0.690490 -15.941538 0.691598 -15.939880 c
0.693813 -15.936565 0.696498 -15.932516 0.699638 -15.927732 c
0.705917 -15.918165 0.714015 -15.905657 0.723805 -15.890184 c
0.743388 -15.859243 0.769744 -15.816458 0.801883 -15.761660 c
0.866165 -15.652058 0.953561 -15.494444 1.056147 -15.287495 c
1.261366 -14.873503 1.527114 -14.262609 1.790166 -13.444414 c
2.316660 -11.806815 2.830000 -9.346090 2.830000 -5.980957 c
1.170000 -5.980957 l
h
f
n
Q
q
0.000000 -1.000000 1.000000 0.000000 20.980957 22.000000 cm
0.000000 0.000000 0.000000 scn
0.000000 3.519043 m
-0.686377 3.052367 -0.686606 3.052704 -0.686827 3.053029 c
-0.686890 3.053123 -0.687103 3.053437 -0.687230 3.053624 c
-0.687484 3.053999 -0.687707 3.054330 -0.687901 3.054617 c
-0.688287 3.055190 -0.688552 3.055586 -0.688699 3.055805 c
-0.688991 3.056243 -0.688808 3.055973 -0.688163 3.054991 c
-0.686874 3.053027 -0.683741 3.048217 -0.678884 3.040542 c
-0.669169 3.025192 -0.652556 2.998377 -0.630008 2.959931 c
-0.584915 2.883047 -0.516061 2.759615 -0.431147 2.588317 c
-0.261366 2.245815 -0.027114 1.711416 0.209833 0.974417 c
0.683340 -0.498374 1.170000 -2.787649 1.170000 -5.980957 c
2.830000 -5.980957 l
2.830000 -2.615824 2.316660 -0.155099 1.790166 1.482499 c
1.527114 2.300695 1.261366 2.911589 1.056147 3.325581 c
0.953561 3.532529 0.866165 3.690144 0.801883 3.799746 c
0.769744 3.854543 0.743388 3.897330 0.723805 3.928271 c
0.714015 3.943742 0.705917 3.956251 0.699638 3.965818 c
0.696498 3.970602 0.693813 3.974651 0.691598 3.977967 c
0.690490 3.979625 0.689500 3.981099 0.688629 3.982391 c
0.688194 3.983037 0.687788 3.983638 0.687413 3.984192 c
0.687225 3.984469 0.686966 3.984851 0.686872 3.984990 c
0.686621 3.985360 0.686377 3.985719 0.000000 3.519043 c
h
1.170000 -5.980957 m
1.170000 -9.174265 0.683340 -11.463540 0.209833 -12.936331 c
-0.027114 -13.673330 -0.261366 -14.207729 -0.431147 -14.550232 c
-0.516061 -14.721529 -0.584915 -14.844961 -0.630008 -14.921844 c
-0.652556 -14.960291 -0.669169 -14.987106 -0.678884 -15.002457 c
-0.683741 -15.010132 -0.686874 -15.014940 -0.688163 -15.016905 c
-0.688808 -15.017887 -0.688991 -15.018158 -0.688699 -15.017719 c
-0.688552 -15.017500 -0.688287 -15.017103 -0.687901 -15.016531 c
-0.687707 -15.016245 -0.687484 -15.015913 -0.687230 -15.015539 c
-0.687103 -15.015350 -0.686890 -15.015036 -0.686827 -15.014942 c
-0.686606 -15.014618 -0.686377 -15.014280 0.000000 -15.480957 c
0.686377 -15.947634 0.686621 -15.947273 0.686872 -15.946903 c
0.686966 -15.946766 0.687225 -15.946383 0.687413 -15.946106 c
0.687788 -15.945551 0.688194 -15.944952 0.688629 -15.944305 c
0.689500 -15.943014 0.690490 -15.941538 0.691598 -15.939880 c
0.693813 -15.936565 0.696498 -15.932516 0.699638 -15.927732 c
0.705917 -15.918165 0.714015 -15.905657 0.723805 -15.890184 c
0.743388 -15.859243 0.769744 -15.816458 0.801883 -15.761660 c
0.866165 -15.652058 0.953561 -15.494444 1.056147 -15.287495 c
1.261366 -14.873503 1.527114 -14.262609 1.790166 -13.444414 c
2.316660 -11.806815 2.830000 -9.346090 2.830000 -5.980957 c
1.170000 -5.980957 l
h
f
n
Q
endstream
endobj
3 0 obj
7785
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000007875 00000 n
0000007898 00000 n
0000008071 00000 n
0000008145 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
8204
%%EOF

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -89,7 +89,7 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
self.interfaceInteraction?.sendBotStart(presentationInterfaceState.botStartPayload)
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState
}

View File

@ -115,7 +115,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
}
}
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) -> (CGFloat, CGFloat) {
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) {
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: UIScreenPixel)))
let updatedTheme = self.theme !== interfaceState.theme

View File

@ -265,7 +265,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: interfaceState, metrics: metrics, force: false)
}

View File

@ -532,8 +532,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
return
}
if transition.isAnimated {
strongSelf.scheduleLayoutTransitionRequest(transition)
} else {
strongSelf.requestLayout(transition)
}
}
self.addSubnode(self.inputPanelContainerNode)
@ -592,6 +596,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.textInputPanelNode?.updateActivity = { [weak self] in
self?.updateTypingActivity(true)
}
self.textInputPanelNode?.toggleExpandMediaInput = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.inputPanelContainerNode.toggleIfEnabled()
}
self.inputMediaNodeDataDisposable = (self.inputMediaNodeDataPromise.get()
|> deliverOnMainQueue).start(next: { [weak self] value in
@ -1056,7 +1066,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if inputTextPanelNode.isFocused {
self.context.sharedContext.mainWindow?.simulateKeyboardDismiss(transition: .animated(duration: 0.5, curve: .spring))
}
let _ = inputTextPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
let _ = inputTextPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: self.inputPanelContainerNode.expansionFraction == 1.0)
}
if let prevInputPanelNode = self.inputPanelNode, inputPanelNode.canHandleTransition(from: prevInputPanelNode) {
inputPanelNodeHandlesTransition = true
@ -1066,7 +1076,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} else {
dismissedInputPanelNode = self.inputPanelNode
}
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: false, transition: inputPanelNode.supernode !== self ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: false, transition: inputPanelNode.supernode !== self ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: self.inputPanelContainerNode.expansionFraction == 1.0)
inputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
self.inputPanelNode = inputPanelNode
if inputPanelNode.supernode !== self {
@ -1074,7 +1084,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.inputPanelClippingNode.insertSubnode(inputPanelNode, aboveSubnode: self.inputPanelBackgroundNode)
}
} else {
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset - 120.0, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset - 120.0, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: self.inputPanelContainerNode.expansionFraction == 1.0)
inputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
}
} else {
@ -1085,7 +1095,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if let secondaryInputPanelNode = inputPanelNodes.secondary, !previewing {
if secondaryInputPanelNode !== self.secondaryInputPanelNode {
dismissedSecondaryInputPanelNode = self.secondaryInputPanelNode
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: true, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: true, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: self.inputPanelContainerNode.expansionFraction == 1.0)
secondaryInputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
self.secondaryInputPanelNode = secondaryInputPanelNode
if secondaryInputPanelNode.supernode == nil {
@ -1093,7 +1103,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.inputPanelClippingNode.insertSubnode(secondaryInputPanelNode, aboveSubnode: self.inputPanelBackgroundNode)
}
} else {
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: true, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - inputPanelBottomInset, isSecondary: true, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: self.inputPanelContainerNode.expansionFraction == 1.0)
secondaryInputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
}
} else {
@ -1142,8 +1152,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
var maximumInputNodeHeight = layout.size.height - max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top) - 10.0
if let inputPanelSize = inputPanelSize {
if let inputNode = self.inputNode, inputNode.hideInput, !inputNode.adjustLayoutForHiddenInput {
maximumInputNodeHeight -= inputPanelNodeBaseHeight
} else {
maximumInputNodeHeight -= inputPanelSize.height
}
}
if let secondaryInputPanelSize = secondaryInputPanelSize {
maximumInputNodeHeight -= secondaryInputPanelSize.height
}
@ -1210,13 +1224,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
if inputNode.hideInput, let inputPanelSize = inputPanelSize {
if inputNode.hideInput, inputNode.adjustLayoutForHiddenInput, let inputPanelSize = inputPanelSize {
maximumInputNodeHeight += inputPanelSize.height
}
let inputHeight = layout.standardInputHeight + self.inputPanelContainerNode.expansionFraction * (maximumInputNodeHeight - layout.standardInputHeight)
let heightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: inputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus)
let heightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: inputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
let boundedHeight = min(heightAndOverflow.0, layout.standardInputHeight)
@ -1267,7 +1281,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
if let inputMediaNode = self.inputMediaNode, inputMediaNode != self.inputNode {
let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: false)
let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
}
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 66.0)))
@ -1358,8 +1372,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if self.dismissedAsOverlay {
inputPanelFrame!.origin.y = layout.size.height
}
if let inputNode = self.inputNode, inputNode.hideInput, !inputNode.adjustLayoutForHiddenInput {
inputPanelsHeight += inputPanelNodeBaseHeight
} else {
inputPanelsHeight += inputPanelSize!.height
}
}
if self.secondaryInputPanelNode != nil {
secondaryInputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight - secondaryInputPanelSize!.height), size: CGSize(width: layout.size.width, height: secondaryInputPanelSize!.height))
@ -1384,7 +1402,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
if let inputNode = self.inputNode {
if inputNode.hideInput {
if inputNode.hideInput && inputNode.adjustLayoutForHiddenInput {
inputPanelsHeight = 0.0
}
}
@ -1963,7 +1981,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
private func updateInputPanelBackgroundExpansion(transition: ContainedViewLayoutTransition) {
if let inputNode = self.inputNode {
if inputNode.hideInput {
if inputNode.hideInput && inputNode.adjustLayoutForHiddenInput {
self.storedHideInputExpanded = self.inputPanelContainerNode.expansionFraction == 1.0
self.inputPanelContainerNode.expand()
} else {
@ -2270,6 +2288,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.inputPanelContainerNode.collapse()
if let inputNode = self.inputNode {
inputNode.hideInput = false
inputNode.adjustLayoutForHiddenInput = false
if let inputNode = inputNode as? ChatEntityKeyboardInputNode {
inputNode.markInputCollapsed()
}
@ -2344,7 +2363,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
self.inputMediaNode = inputNode
if let (validLayout, _) = self.validLayout {
let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: validLayout.deviceMetrics, isVisible: false)
let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: validLayout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
}
}

View File

@ -14,6 +14,7 @@ import TelegramCore
import ComponentDisplayAdapters
import SettingsUI
import TextFormat
import PagerComponent
final class ChatEntityKeyboardInputNode: ChatInputNode {
struct InputData: Equatable {
@ -215,11 +216,11 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
var title: String?
if group.id == AnyHashable("recent") {
//TODO:localize
title = "Recently Used".uppercased()
title = "Recently Used"
} else {
for (id, info, _) in view.collectionInfos {
if AnyHashable(id) == group.id, let info = info as? StickerPackCollectionInfo {
title = info.title.uppercased()
title = info.title
break
}
}
@ -396,17 +397,17 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
var title: String?
if group.id == AnyHashable("saved") {
//TODO:localize
title = "Saved".uppercased()
title = "Saved"
} else if group.id == AnyHashable("recent") {
//TODO:localize
title = "Recently Used".uppercased()
title = "Recently Used"
} else if group.id == AnyHashable("premium") {
//TODO:localize
title = "Premium".uppercased()
title = "Premium"
} else {
for (id, info, _) in view.collectionInfos {
if AnyHashable(id) == group.id, let info = info as? StickerPackCollectionInfo {
title = info.title.uppercased()
title = info.title
break
}
}
@ -462,7 +463,12 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
private var isMarkInputCollapsed: Bool = false
private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool)?
var externalTopPanelContainerImpl: PagerExternalTopPanelContainer?
override var externalTopPanelContainer: UIView? {
return self.externalTopPanelContainerImpl
}
private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)?
init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, controllerInteraction: ChatControllerInteraction) {
self.context = context
@ -477,7 +483,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
self.view.addSubview(self.entityKeyboardView)
self.externalTopPanelContainer = SparseContainerView()
self.externalTopPanelContainerImpl = PagerExternalTopPanelContainer()
self.inputDataDisposable = (updatedInputData
|> deliverOnMainQueue).start(next: { [weak self] inputData in
@ -530,20 +536,25 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
}
private func performLayout() {
guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.currentState else {
guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, 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)
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) -> (CGFloat, CGFloat) {
self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible)
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)
let wasMarkedInputCollapsed = self.isMarkInputCollapsed
self.isMarkInputCollapsed = false
let expandedHeight = standardInputHeight + self.expansionFraction * (maximumHeight - standardInputHeight)
var hiddenInputHeight: CGFloat = 0.0
if self.hideInput && !self.adjustLayoutForHiddenInput {
hiddenInputHeight = inputPanelHeight
}
let context = self.context
let controllerInteraction = self.controllerInteraction
let inputNodeInteraction = self.inputNodeInteraction!
@ -564,7 +575,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
stickerContent: self.currentInputData.stickers,
gifContent: self.currentInputData.gifs,
defaultToEmojiTab: self.defaultToEmojiTab,
externalTopPanelContainer: self.externalTopPanelContainer,
externalTopPanelContainer: self.externalTopPanelContainerImpl,
topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in
guard let strongSelf = self else {
return
@ -574,15 +585,24 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
strongSelf.topBackgroundExtensionUpdated?(transition.containedViewLayoutTransition)
}
},
hideInputUpdated: { [weak self] hideInput, transition in
hideInputUpdated: { [weak self] hideInput, adjustLayout, transition in
guard let strongSelf = self else {
return
}
if strongSelf.hideInput != hideInput {
if strongSelf.hideInput != hideInput || strongSelf.adjustLayoutForHiddenInput != adjustLayout {
strongSelf.hideInput = hideInput
strongSelf.adjustLayoutForHiddenInput = adjustLayout
strongSelf.hideInputUpdated?(transition.containedViewLayoutTransition)
}
},
switchToTextInput: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.controllerInteraction.updateInputMode { _ in
return .text
}
},
makeSearchContainerNode: { content in
let mappedMode: ChatMediaInputSearchMode
switch content {
@ -605,7 +625,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
}
)
},
deviceMetrics: deviceMetrics
deviceMetrics: deviceMetrics,
hiddenInputHeight: hiddenInputHeight,
isExpanded: isExpanded
)),
environment: {},
containerSize: CGSize(width: width, height: expandedHeight)

View File

@ -54,7 +54,7 @@ final class ChatFeedNavigationInputPanelNode: ChatInputPanelNode {
self.interfaceInteraction?.navigateFeed()
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState
}

View File

@ -11,18 +11,21 @@ class ChatInputNode: ASDisplayNode {
return .single(Void())
}
var externalTopPanelContainer: UIView?
var externalTopPanelContainer: UIView? {
return nil
}
var topBackgroundExtension: CGFloat = 41.0
var topBackgroundExtensionUpdated: ((ContainedViewLayoutTransition) -> Void)?
var hideInput: Bool = false
var adjustLayoutForHiddenInput: Bool = false
var hideInputUpdated: ((ContainedViewLayoutTransition) -> Void)?
var expansionFraction: CGFloat = 0.0
var expansionFractionUpdated: ((ContainedViewLayoutTransition) -> Void)?
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) -> (CGFloat, CGFloat) {
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) {
return (0.0, 0.0)
}
}

View File

@ -15,7 +15,7 @@ class ChatInputPanelNode: ASDisplayNode {
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
}
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
return 0.0
}

View File

@ -302,6 +302,9 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
}
var stickersEnabled = true
let stickersAreEmoji = !isTextEmpty
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
if isTextEmpty, case .broadcast = peer.info, canSendMessagesToPeer(peer) {
accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
@ -318,7 +321,7 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
accessoryItems.append(.commands)
}
accessoryItems.append(.stickers(stickersEnabled))
accessoryItems.append(.stickers(isEnabled: stickersEnabled, isEmoji: stickersAreEmoji))
if isTextEmpty, let message = chatPresentationInterfaceState.keyboardButtonsMessage, let _ = message.visibleButtonKeyboardMarkup, chatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId != message.id {
accessoryItems.append(.inputButtons)

View File

@ -577,7 +577,7 @@ final class ChatMediaInputNode: ChatInputNode {
var requestDisableStickerAnimations: ((Bool) -> Void)?
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool)?
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool, Bool)?
private var paneArrangement: ChatMediaInputPaneArrangement
private var initializedArrangement = false
@ -1473,7 +1473,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
})))
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = strongSelf.validLayout {
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true
@ -1595,7 +1595,7 @@ final class ChatMediaInputNode: ChatInputNode {
|> map { isStarred -> (UIView, CGRect, PeekControllerContent)? in
if let strongSelf = self {
var menuItems: [ContextMenuItem] = []
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = strongSelf.validLayout {
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true
@ -1747,7 +1747,7 @@ final class ChatMediaInputNode: ChatInputNode {
|> map { isStarred, hasPremium -> (UIView, CGRect, PeekControllerContent)? in
if let strongSelf = self {
var menuItems: [ContextMenuItem] = []
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = strongSelf.validLayout {
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true
@ -1922,8 +1922,8 @@ final class ChatMediaInputNode: ChatInputNode {
self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index)
let updatedGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
self.updateAppearanceTransition(transition: transition)
}
if updatedGifPanelWasActive != previousGifPanelWasActive {
@ -1940,8 +1940,8 @@ final class ChatMediaInputNode: ChatInputNode {
}
}
} else {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
}
}
}
@ -2179,20 +2179,20 @@ final class ChatMediaInputNode: ChatInputNode {
}
func simulateUpdateLayout(isVisible: Bool) {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _) = self.validLayout {
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)
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _, isExpanded) = self.validLayout {
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) -> (CGFloat, CGFloat) {
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) {
var searchMode: ChatMediaInputSearchMode?
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = self.validLayout, case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = self.validLayout, case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {
searchMode = mode
}
let wasVisible = self.validLayout?.10 ?? false
self.validLayout = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible)
self.validLayout = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded)
if self.theme !== interfaceState.theme || self.strings !== interfaceState.strings {
self.updateThemeAndStrings(chatWallpaper: interfaceState.chatWallpaper, theme: interfaceState.theme, strings: interfaceState.strings)
@ -2545,7 +2545,7 @@ final class ChatMediaInputNode: ChatInputNode {
self.stickerPane.removeFromSupernode()
}
case .changed:
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let translationX = -recognizer.translation(in: self.view).x
var indexTransition = translationX / width
if self.paneArrangement.currentIndex == 0 {
@ -2554,10 +2554,10 @@ final class ChatMediaInputNode: ChatInputNode {
indexTransition = min(0.0, indexTransition)
}
self.paneArrangement = self.paneArrangement.withIndexTransition(indexTransition)
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)
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)
}
case .ended:
if let (width, _, _, _, _, _, _, _, _, _, _) = self.validLayout {
if let (width, _, _, _, _, _, _, _, _, _, _, _) = self.validLayout {
var updatedIndex = self.paneArrangement.currentIndex
if abs(self.paneArrangement.indexTransition * width) > 30.0 {
if self.paneArrangement.indexTransition < 0.0 {
@ -2570,9 +2570,9 @@ final class ChatMediaInputNode: ChatInputNode {
self.setCurrentPane(self.paneArrangement.panes[updatedIndex], transition: .animated(duration: 0.25, curve: .spring))
}
case .cancelled:
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout {
self.paneArrangement = self.paneArrangement.withIndexTransition(0.0)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
}
default:
break

View File

@ -71,7 +71,7 @@ final class ChatMessageReportInputPanelNode: ChatInputPanelNode {
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState

View File

@ -17,7 +17,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
private let shareButton: HighlightableButtonNode
private let separatorNode: ASDisplayNode
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool)?
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool, isMediaInputExpanded: Bool)?
private var presentationInterfaceState: ChatPresentationInterfaceState?
private var actions: ChatAvailableMessageActions?
@ -33,8 +33,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
if self.selectedMessages.isEmpty {
self.actions = nil
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let interfaceState = self.presentationInterfaceState {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = self.validLayout, let interfaceState = self.presentationInterfaceState {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
self.canDeleteMessagesDisposable.set(nil)
} else if let context = self.context {
@ -42,8 +42,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|> deliverOnMainQueue).start(next: { [weak self] actions in
if let strongSelf = self {
strongSelf.actions = actions
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout, let interfaceState = strongSelf.presentationInterfaceState {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = strongSelf.validLayout, let interfaceState = strongSelf.presentationInterfaceState {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
}
}))
@ -154,8 +154,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded)
let panelHeight = defaultHeight(metrics: metrics)

View File

@ -355,8 +355,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
updatedAttributes[NSAttributedString.Key("Attribute__EmbeddedItem")] = InlineStickerItem(emoji: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId))
updatedAttributes[ChatTextInputAttributes.customEmoji] = ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId)
let insertString = NSAttributedString(string: "[\u{00a0}\u{00a0}\u{00a0}]", attributes: updatedAttributes)
let insertString = NSAttributedString(string: updatedString.attributedSubstring(from: range).string, attributes: updatedAttributes)
updatedString.replaceCharacters(in: range, with: insertString)
}
attributedText = updatedString

View File

@ -135,7 +135,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
var updateWaveform = false
if self.presentationInterfaceState?.recordedMediaPreview != interfaceState.recordedMediaPreview {

View File

@ -23,7 +23,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
self.addSubnode(self.textNode)
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState
}

View File

@ -28,7 +28,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
private var needsSearchResultsTooltip = true
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool)?
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool, isMediaInputExpanded: Bool)?
override var interfaceInteraction: ChatPanelInterfaceInteraction? {
didSet {
@ -38,8 +38,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
if let strongSelf = self, strongSelf.displayActivity != value {
strongSelf.displayActivity = value
strongSelf.activityIndicator.isHidden = !value
if let interfaceState = strongSelf.presentationInterfaceState, let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
if let interfaceState = strongSelf.presentationInterfaceState, let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
}
}))
@ -127,8 +127,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded)
if self.presentationInterfaceState != interfaceState {
let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme

View File

@ -132,7 +132,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.backdropNode.update(rect: rect, within: containerSize, transition: transition)
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
func updateLayout(size: CGSize, isMediaInputExpanded: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
self.validLayout = size
transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
self.micButton.layoutItems()
@ -183,10 +183,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
}
transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
var expanded = false
if case let .media(_, maybeExpanded, _) = interfaceState.inputMode, maybeExpanded != nil {
expanded = true
}
let expanded = isMediaInputExpanded
transition.updateSublayerTransformScale(node: self.expandMediaInputButton, scale: CGPoint(x: 1.0, y: expanded ? 1.0 : -1.0))
}

View File

@ -25,17 +25,25 @@ import ManagedAnimationNode
import AttachmentUI
import EditableChatTextNode
import EmojiTextAttachmentView
import LottieAnimationComponent
import ComponentFlow
private let accessoryButtonFont = Font.medium(14.0)
private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers])
private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
private let item: ChatTextInputAccessoryItem
private var item: ChatTextInputAccessoryItem
private var theme: PresentationTheme
private var width: CGFloat
private let iconImageNode: ASImageNode
private var animationView: ComponentView<Empty>?
private var imageEdgeInsets = UIEdgeInsets()
init(item: ChatTextInputAccessoryItem, theme: PresentationTheme, strings: PresentationStrings) {
self.item = item
self.theme = theme
self.iconImageNode = ASImageNode()
let (image, text, accessibilityLabel, alpha, insets) = AccessoryItemIconButtonNode.imageAndInsets(item: item, theme: theme, strings: strings)
@ -46,7 +54,13 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
self.isAccessibilityElement = true
self.accessibilityTraits = [.button]
self.addSubnode(self.imageNode)
self.iconImageNode.isUserInteractionEnabled = false
self.addSubnode(self.iconImageNode)
if case .stickers = item {
self.iconImageNode.isHidden = true
self.animationView = ComponentView<Empty>()
}
if let text = text {
self.setAttributedTitle(NSAttributedString(string: text, font: accessoryButtonFont, textColor: theme.chat.inputPanel.inputControlColor), for: .normal)
@ -54,8 +68,8 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
self.setAttributedTitle(NSAttributedString(), for: .normal)
}
self.imageNode.image = image
self.imageNode.alpha = alpha
self.iconImageNode.image = image
self.iconImageNode.alpha = alpha
self.imageEdgeInsets = insets
self.accessibilityLabel = accessibilityLabel
@ -74,6 +88,8 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
}
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
self.theme = theme
let (image, text, accessibilityLabel, alpha, insets) = AccessoryItemIconButtonNode.imageAndInsets(item: item, theme: theme, strings: strings)
self.width = AccessoryItemIconButtonNode.calculateWidth(item: item, image: image, text: text, strings: strings)
@ -84,9 +100,9 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
self.setAttributedTitle(NSAttributedString(), for: .normal)
}
self.imageNode.image = image
self.iconImageNode.image = image
self.imageEdgeInsets = insets
self.imageNode.alpha = alpha
self.iconImageNode.alpha = alpha
self.accessibilityLabel = accessibilityLabel
}
@ -99,7 +115,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
switch item {
case .keyboard:
return (PresentationResourcesChat.chatInputTextFieldKeyboardImage(theme), nil, strings.VoiceOver_Keyboard, 1.0, UIEdgeInsets())
case let .stickers(enabled):
case let .stickers(enabled, _):
return (PresentationResourcesChat.chatInputTextFieldStickersImage(theme), nil, strings.VoiceOver_Stickers, enabled ? 1.0 : 0.4, UIEdgeInsets())
case .inputButtons:
return (PresentationResourcesChat.chatInputTextFieldInputButtonsImage(theme), nil, strings.VoiceOver_BotKeyboard, 1.0, UIEdgeInsets())
@ -136,11 +152,48 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
}
}
func updateLayout(size: CGSize) {
if let image = self.imageNode.image {
func updateLayout(item: ChatTextInputAccessoryItem, size: CGSize) {
self.item = item
if let image = self.iconImageNode.image {
let bottomInset: CGFloat = 0.0
let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0) - bottomInset), size: image.size)
self.imageNode.frame = imageFrame
self.iconImageNode.frame = imageFrame
if case let .stickers(_, isEmoji) = item, let animationView = self.animationView {
let animationFrame = imageFrame.insetBy(dx: -4.0, dy: -4.0)
var colors: [String: UIColor] = [:]
let colorKeys: [String] = [
"Ellipse 33.Ellipse 33.Stroke 1",
"Ellipse 34.Ellipse 34.Stroke 1",
"Oval.Oval.Fill 1",
"Oval 2.Oval.Fill 1",
"Path 85.Path 85.Stroke 1"
]
for colorKey in colorKeys {
colors[colorKey] = self.theme.chat.inputPanel.inputControlColor
}
let _ = animationView.update(
transition: .immediate,
component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
name: !isEmoji ? "anim_stickertosmile" : "anim_smiletosticker",
colors: colors,
mode: .animateTransitionFromPrevious
),
size: animationFrame.size
)),
environment: {},
containerSize: animationFrame.size
)
if let view = animationView.view {
if view.superview == nil {
self.view.addSubview(view)
}
view.frame = animationFrame
}
}
}
}
@ -196,7 +249,7 @@ private func calculateTextFieldRealInsets(presentationInterfaceState: ChatPresen
}
var right: CGFloat = 0.0
right += accessoryButtonsWidth
right += max(0.0, accessoryButtonsWidth - 14.0)
return UIEdgeInsets(top: 4.5 + top, left: 0.0, bottom: 5.5 + bottom, right: right)
}
@ -347,13 +400,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
private var accessoryItemButtons: [(ChatTextInputAccessoryItem, AccessoryItemIconButtonNode)] = []
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool, Bool)?
private var leftMenuInset: CGFloat = 0.0
var displayAttachmentMenu: () -> Void = { }
var sendMessage: () -> Void = { }
var paste: (ChatTextInputPanelPasteData) -> Void = { _ in }
var updateHeight: (Bool) -> Void = { _ in }
var toggleExpandMediaInput: (() -> Void)?
var updateActivity: () -> Void = { }
@ -450,8 +504,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
for item in accessoryItems {
var itemAndButton: (ChatTextInputAccessoryItem, AccessoryItemIconButtonNode)?
for i in 0 ..< self.accessoryItemButtons.count {
if self.accessoryItemButtons[i].0 == item {
if self.accessoryItemButtons[i].0.key == item.key {
itemAndButton = self.accessoryItemButtons[i]
itemAndButton?.0 = item
self.accessoryItemButtons.remove(at: i)
break
}
@ -661,15 +716,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
self.actionButtons.micButton.offsetRecordingControls = { [weak self] in
if let strongSelf = self, let presentationInterfaceState = strongSelf.presentationInterfaceState {
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
}
}
self.actionButtons.micButton.updateCancelTranslation = { [weak self] in
if let strongSelf = self, let presentationInterfaceState = strongSelf.presentationInterfaceState {
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) = strongSelf.validLayout {
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded)
}
}
}
@ -911,10 +966,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.actionButtons.updateAbsoluteRect(CGRect(origin: rect.origin.offsetBy(dx: self.actionButtons.frame.minX, dy: self.actionButtons.frame.minY), size: self.actionButtons.frame.size), within: containerSize, transition: transition)
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
let previousAdditionalSideInsets = self.validLayout?.4
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded)
let textFieldWaitsForTouchUp: Bool
if case .regular = metrics.widthClass, bottomInset.isZero {
@ -1228,8 +1282,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
for item in interfaceState.inputTextPanelState.accessoryItems {
var itemAndButton: (ChatTextInputAccessoryItem, AccessoryItemIconButtonNode)?
for i in 0 ..< self.accessoryItemButtons.count {
if self.accessoryItemButtons[i].0 == item {
if self.accessoryItemButtons[i].0.key == item.key {
itemAndButton = self.accessoryItemButtons[i]
itemAndButton?.0 = item
self.accessoryItemButtons.remove(at: i)
break
}
@ -1661,7 +1716,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
if let presentationInterfaceState = self.presentationInterfaceState {
self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), transition: transition, interfaceState: presentationInterfaceState)
self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), isMediaInputExpanded: isMediaInputExpanded, transition: transition, interfaceState: presentationInterfaceState)
}
if let _ = interfaceState.inputTextPanelState.mediaRecordingState {
@ -1784,9 +1839,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
var nextButtonTopRight = CGPoint(x: width - rightInset - textFieldInsets.right - accessoryButtonInset, y: panelHeight - textFieldInsets.bottom - minimalInputHeight)
for (_, button) in self.accessoryItemButtons.reversed() {
for (item, button) in self.accessoryItemButtons.reversed() {
let buttonSize = CGSize(width: button.buttonWidth, height: minimalInputHeight)
button.updateLayout(size: buttonSize)
button.updateLayout(item: item, size: buttonSize)
let buttonFrame = CGRect(origin: CGPoint(x: nextButtonTopRight.x - buttonSize.width, y: nextButtonTopRight.y + floor((minimalInputHeight - buttonSize.height) / 2.0)), size: buttonSize)
if button.supernode == nil {
self.clippingNode.addSubnode(button)
@ -2159,7 +2214,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.counterTextNode.attributedText = NSAttributedString(string: "", font: counterFont, textColor: .black)
}
if let (width, leftInset, rightInset, _, _, maxHeight, metrics, _) = self.validLayout {
if let (width, leftInset, rightInset, _, _, maxHeight, metrics, _, _) = self.validLayout {
var composeButtonsOffset: CGFloat = 0.0
if self.extendedSearchLayout {
composeButtonsOffset = 44.0
@ -2299,8 +2354,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
}
let hideExpandMediaInput = hideMicButton
if mediaInputIsActive {
//hideMicButton = true
hideMicButton = true
}
if hideMicButton {
@ -2325,7 +2382,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
}
if mediaInputIsActive && !"".isEmpty {
if mediaInputIsActive && !hideExpandMediaInput {
if self.actionButtons.expandMediaInputButton.alpha.isZero {
self.actionButtons.expandMediaInputButton.alpha = 1.0
if animated {
@ -2350,7 +2407,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
private func updateTextHeight(animated: Bool) {
if let (width, leftInset, rightInset, _, additionalSideInsets, maxHeight, metrics, _) = self.validLayout {
if let (width, leftInset, rightInset, _, additionalSideInsets, maxHeight, metrics, _, _) = self.validLayout {
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right - self.leftMenuInset, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
if !self.bounds.size.height.isEqual(to: panelHeight) {
@ -2836,7 +2893,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
@objc func expandButtonPressed() {
self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
self.toggleExpandMediaInput?()
/*self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
if case let .media(mode, expanded, focused) = state.inputMode {
if let _ = expanded {
return (.media(mode: mode, expanded: nil, focused: focused), state.interfaceState.messageActionsState.closedButtonKeyboardMessageId)
@ -2846,14 +2904,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} else {
return (state.inputMode, state.interfaceState.messageActionsState.closedButtonKeyboardMessageId)
}
})
})*/
}
@objc func accessoryItemButtonPressed(_ button: UIView) {
for (item, currentButton) in self.accessoryItemButtons {
if currentButton === button {
switch item {
case let .stickers(enabled):
case let .stickers(enabled, _):
if enabled {
self.interfaceInteraction?.openStickers()
} else {

View File

@ -82,7 +82,7 @@ final class ChatUnblockInputPanelNode: ChatInputPanelNode {
self.interfaceInteraction?.unblockPeer()
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState

View File

@ -35,7 +35,7 @@ final class DeleteChatInputPanelNode: ChatInputPanelNode {
self.interfaceInteraction?.deleteChat()
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState

View File

@ -379,7 +379,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics)
let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics, isMediaInputExpanded: false)
transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight)))

View File

@ -491,7 +491,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if textInputPanelNode.frame.width.isZero {
panelTransition = .immediate
}
var panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
var panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics, isMediaInputExpanded: false)
if self.searchDisplayController == nil {
panelHeight += insets.bottom
} else {

View File

@ -44,7 +44,7 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
self.interfaceInteraction?.unblockPeer()
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState

View File

@ -249,6 +249,8 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
string.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Timecode), value: TelegramTimecode(time: time, text: text), range: range)
}
}
case let .CustomEmoji(stickerPack, fileId):
string.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId), range: range)
default:
break
}