Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,43 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PeerAllowedReactionsScreen",
module_name = "PeerAllowedReactionsScreen",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display:Display",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/AppBundle",
"//submodules/Components/ViewControllerComponent",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/AccountContext",
"//submodules/TelegramUI/Components/EntityKeyboard",
"//submodules/TelegramUI/Components/SwitchComponent",
"//submodules/Components/MultilineTextComponent",
"//submodules/Markdown",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
"//submodules/Components/BundleIconComponent",
"//submodules/Components/PagerComponent",
"//submodules/PremiumUI",
"//submodules/UndoUI",
"//submodules/TextFormat",
"//submodules/Components/HierarchyTrackingLayer",
"//submodules/TelegramUI/Components/ListSectionComponent",
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
"//submodules/TelegramUI/Components/ListSwitchItemComponent",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,346 @@
import Foundation
import UIKit
import Display
import TelegramPresentationData
import ComponentFlow
import ComponentDisplayAdapters
import SwitchComponent
import EntityKeyboard
import AccountContext
import HierarchyTrackingLayer
import TelegramCore
private final class CaretIndicatorView: UIImageView {
private let hierarchyTrackingLayer: HierarchyTrackingLayer
override init(frame: CGRect) {
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
super.init(frame: frame)
self.layer.addSublayer(self.hierarchyTrackingLayer)
self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
self?.restartAnimations(delayStart: false)
}
}
required init?(coder: NSCoder) {
preconditionFailure()
}
func restartAnimations(delayStart: Bool) {
self.layer.removeAnimation(forKey: "caret")
let animation = CAKeyframeAnimation(keyPath: "opacity")
animation.values = [1.0 as NSNumber, 0.0 as NSNumber, 1.0 as NSNumber, 1.0 as NSNumber]
let firstDuration = 0.3
let secondDuration = 0.25
let restDuration = 0.35
let duration = firstDuration + secondDuration + restDuration
let keyTimes: [NSNumber] = [0.0 as NSNumber, (firstDuration / duration) as NSNumber, ((firstDuration + secondDuration) / duration) as NSNumber, ((firstDuration + secondDuration + restDuration) / duration) as NSNumber]
animation.keyTimes = keyTimes
animation.duration = duration
animation.repeatCount = Float.greatestFiniteMagnitude
animation.fillMode = .both
if delayStart {
animation.beginTime = self.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.8 * UIView.animationDurationFactor()
}
self.layer.add(animation, forKey: "caret")
}
}
final class EmojiListInputComponent: Component {
let context: AccountContext
let theme: PresentationTheme
let placeholder: String
let reactionItems: [EmojiComponentReactionItem]
let isInputActive: Bool
let caretPosition: Int
let activateInput: () -> Void
let setCaretPosition: (Int) -> Void
init(
context: AccountContext,
theme: PresentationTheme,
placeholder: String,
reactionItems: [EmojiComponentReactionItem],
isInputActive: Bool,
caretPosition: Int,
activateInput: @escaping () -> Void,
setCaretPosition: @escaping (Int) -> Void
) {
self.context = context
self.theme = theme
self.placeholder = placeholder
self.reactionItems = reactionItems
self.isInputActive = isInputActive
self.caretPosition = caretPosition
self.activateInput = activateInput
self.setCaretPosition = setCaretPosition
}
static func ==(lhs: EmojiListInputComponent, rhs: EmojiListInputComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.placeholder != rhs.placeholder {
return false
}
if lhs.reactionItems != rhs.reactionItems {
return false
}
if lhs.isInputActive != rhs.isInputActive {
return false
}
if lhs.caretPosition != rhs.caretPosition {
return false
}
return true
}
final class View: UIView {
private var component: EmojiListInputComponent?
private weak var state: EmptyComponentState?
private var itemLayers: [Int64: EmojiKeyboardItemLayer] = [:]
private let trailingPlaceholder = ComponentView<Empty>()
private let caretIndicator: CaretIndicatorView
override init(frame: CGRect) {
self.caretIndicator = CaretIndicatorView(frame: CGRect())
self.caretIndicator.image = generateImage(CGSize(width: 2.0, height: 4.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.width * 0.5).cgPath)
context.fillPath()
})?.stretchableImage(withLeftCapWidth: 1, topCapHeight: 2).withRenderingMode(.alwaysTemplate)
super.init(frame: frame)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
required init(coder: NSCoder) {
preconditionFailure()
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
guard let component = self.component else {
return
}
if case .ended = recognizer.state {
let point = recognizer.location(in: self)
var tapOnItem = false
for (itemId, itemLayer) in self.itemLayers {
if itemLayer.frame.insetBy(dx: -6.0, dy: -6.0).contains(point) {
if let itemIndex = component.reactionItems.firstIndex(where: { $0.file.fileId.id == itemId }) {
var caretPosition = point.x >= itemLayer.frame.midX ? (itemIndex + 1) : itemIndex
caretPosition = max(0, min(component.reactionItems.count, caretPosition))
component.setCaretPosition(caretPosition)
component.activateInput()
}
tapOnItem = true
break
}
}
if !tapOnItem {
component.setCaretPosition(component.reactionItems.count)
component.activateInput()
}
}
}
func caretRect() -> CGRect? {
if !self.caretIndicator.isHidden {
return self.caretIndicator.frame
} else {
return nil
}
}
func update(component: EmojiListInputComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let verticalInset: CGFloat = 12.0
let placeholderSpacing: CGFloat = 6.0
let minItemSize: CGFloat = 24.0
let itemSpacingFactor: CGFloat = 0.15
let minSideInset: CGFloat = 12.0
self.backgroundColor = component.theme.list.itemBlocksBackgroundColor
self.layer.cornerRadius = 26.0
let maxItemsWidth = availableSize.width - minSideInset * 2.0
let itemsPerRow = Int(floor((maxItemsWidth + minItemSize * itemSpacingFactor) / (minItemSize + minItemSize * itemSpacingFactor)))
let itemSizePlusSpacing = maxItemsWidth / CGFloat(itemsPerRow)
let itemSize = floor(itemSizePlusSpacing * (1.0 - itemSpacingFactor))
let itemSpacing = floor(itemSizePlusSpacing * itemSpacingFactor)
let sideInset = floor((availableSize.width - (itemSize * CGFloat(itemsPerRow) + itemSpacing * CGFloat(itemsPerRow - 1))) * 0.5)
var rowCount = (component.reactionItems.count + (itemsPerRow - 1)) / itemsPerRow
rowCount = max(1, rowCount)
if let previousComponent = self.component, (previousComponent.reactionItems.count != component.reactionItems.count || previousComponent.isInputActive != component.isInputActive) {
self.caretIndicator.restartAnimations(delayStart: true)
}
self.component = component
self.state = state
let trailingPlaceholderSize = self.trailingPlaceholder.update(
transition: .immediate,
component: AnyComponent(Text(text: component.placeholder, font: Font.regular(17.0), color: component.theme.list.itemPlaceholderTextColor)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
)
var lastRowItemCount = component.reactionItems.count % itemsPerRow
if lastRowItemCount == 0 && !component.reactionItems.isEmpty {
lastRowItemCount = itemsPerRow
}
let trailingLineWidth = sideInset + CGFloat(lastRowItemCount) * (itemSize + itemSpacing) + placeholderSpacing
var contentHeight: CGFloat = verticalInset * 2.0 + CGFloat(rowCount) * itemSize + CGFloat(max(0, rowCount - 1)) * itemSpacing
let trailingPlaceholderFrame: CGRect
if availableSize.width - sideInset - trailingLineWidth < trailingPlaceholderSize.width {
contentHeight += itemSize + itemSpacing
trailingPlaceholderFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset + CGFloat(rowCount) * (itemSize + itemSpacing) + floor((itemSize - trailingPlaceholderSize.height) * 0.5)), size: trailingPlaceholderSize)
} else {
trailingPlaceholderFrame = CGRect(origin: CGPoint(x: trailingLineWidth, y: verticalInset + CGFloat(rowCount - 1) * (itemSize + itemSpacing) + floor((itemSize - trailingPlaceholderSize.height) * 0.5)), size: trailingPlaceholderSize)
}
if let trailingPlaceholderView = self.trailingPlaceholder.view {
if trailingPlaceholderView.superview == nil {
trailingPlaceholderView.layer.anchorPoint = CGPoint()
self.addSubview(trailingPlaceholderView)
self.addSubview(self.caretIndicator)
}
transition.setPosition(view: trailingPlaceholderView, position: trailingPlaceholderFrame.origin)
trailingPlaceholderView.bounds = CGRect(origin: CGPoint(), size: trailingPlaceholderFrame.size)
}
self.caretIndicator.tintColor = component.theme.list.itemAccentColor
self.caretIndicator.isHidden = !component.isInputActive
var caretFrame = CGRect(origin: CGPoint(x: trailingPlaceholderFrame.minX, y: trailingPlaceholderFrame.minY + floorToScreenPixels((trailingPlaceholderFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0))
var validIds: [Int64] = []
for i in 0 ..< component.reactionItems.count {
let item = component.reactionItems[i]
let itemKey = item.file.fileId.id
validIds.append(itemKey)
let itemFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i % itemsPerRow) * (itemSize + itemSpacing), y: verticalInset + CGFloat(i / itemsPerRow) * (itemSize + itemSpacing)), size: CGSize(width: itemSize, height: itemSize))
var itemTransition = transition
var animateIn = false
let itemLayer: EmojiKeyboardItemLayer
if let current = self.itemLayers[itemKey] {
itemLayer = current
} else {
itemTransition = .immediate
animateIn = true
let animationData = EntityKeyboardAnimationData(
file: item.file
)
itemLayer = EmojiKeyboardItemLayer(
item: EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: item.file,
subgroupId: nil,
icon: .none,
tintMode: item.file.isCustomTemplateEmoji ? .primary : .none
),
context: component.context,
attemptSynchronousLoad: false,
content: EmojiPagerContentComponent.ItemContent.animation(animationData),
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: component.theme.list.mediaPlaceholderColor,
blurredBadgeColor: .clear,
accentIconColor: component.theme.list.itemPrimaryTextColor,
pointSize: CGSize(width: 32.0, height: 32.0),
onUpdateDisplayPlaceholder: { _, _ in
}
)
self.itemLayers[itemKey] = itemLayer
self.layer.addSublayer(itemLayer)
}
itemLayer.isVisibleForAnimations = true
switch itemLayer.item.tintMode {
case .none:
itemLayer.layerTintColor = nil
case .accent, .primary, .custom:
itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor
}
itemTransition.setFrame(layer: itemLayer, frame: itemFrame)
if component.caretPosition == i {
caretFrame = CGRect(origin: CGPoint(x: itemFrame.minX - 2.0, y: itemFrame.minY + floorToScreenPixels((itemFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0))
} else if i == component.reactionItems.count - 1 && component.caretPosition == i + 1 {
caretFrame = CGRect(origin: CGPoint(x: itemFrame.maxX + itemSpacing, y: itemFrame.minY + floorToScreenPixels((itemFrame.height - 22.0) * 0.5)), size: CGSize(width: 2.0, height: 22.0))
}
if animateIn, !transition.animation.isImmediate {
itemLayer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
itemLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
var removedIds: [Int64] = []
for (key, itemLayer) in self.itemLayers {
if !validIds.contains(key) {
removedIds.append(key)
if !transition.animation.isImmediate {
itemLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak itemLayer] _ in
itemLayer?.removeFromSuperlayer()
})
itemLayer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false)
} else {
itemLayer.removeFromSuperlayer()
}
}
}
for key in removedIds {
self.itemLayers.removeValue(forKey: key)
}
if !transition.animation.isImmediate && abs(caretFrame.midY - self.caretIndicator.center.y) > 2.0 {
if let caretSnapshot = self.caretIndicator.snapshotView(afterScreenUpdates: false) {
caretSnapshot.frame = self.caretIndicator.frame
self.insertSubview(caretSnapshot, aboveSubview: self.caretIndicator)
caretSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak caretSnapshot] _ in
caretSnapshot?.removeFromSuperview()
})
caretSnapshot.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false)
}
self.caretIndicator.frame = caretFrame
self.caretIndicator.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
self.caretIndicator.layer.animateScale(from: 0.001, to: 1.0, duration: 0.15)
} else {
transition.setFrame(view: self.caretIndicator, frame: caretFrame)
}
return CGSize(width: availableSize.width, height: contentHeight)
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
@@ -0,0 +1,325 @@
import Foundation
import UIKit
import Display
import TelegramPresentationData
import ComponentFlow
import ComponentDisplayAdapters
import EntityKeyboard
import AccountContext
import PagerComponent
import AudioToolbox
public final class EmojiSelectionComponent: Component {
public typealias EnvironmentType = Empty
public let theme: PresentationTheme
public let strings: PresentationStrings
public let sideInset: CGFloat
public let bottomInset: CGFloat
public let deviceMetrics: DeviceMetrics
public let emojiContent: EmojiPagerContentComponent?
public let stickerContent: EmojiPagerContentComponent?
public let backgroundIconColor: UIColor?
public let backgroundColor: UIColor
public let separatorColor: UIColor
public let backspace: (() -> Void)?
public init(
theme: PresentationTheme,
strings: PresentationStrings,
sideInset: CGFloat,
bottomInset: CGFloat,
deviceMetrics: DeviceMetrics,
emojiContent: EmojiPagerContentComponent?,
stickerContent: EmojiPagerContentComponent?,
backgroundIconColor: UIColor?,
backgroundColor: UIColor,
separatorColor: UIColor,
backspace: (() -> Void)?
) {
self.theme = theme
self.strings = strings
self.sideInset = sideInset
self.bottomInset = bottomInset
self.deviceMetrics = deviceMetrics
self.emojiContent = emojiContent
self.stickerContent = stickerContent
self.backgroundIconColor = backgroundIconColor
self.backgroundColor = backgroundColor
self.separatorColor = separatorColor
self.backspace = backspace
}
public static func ==(lhs: EmojiSelectionComponent, rhs: EmojiSelectionComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings != rhs.strings {
return false
}
if lhs.sideInset != rhs.sideInset {
return false
}
if lhs.bottomInset != rhs.bottomInset {
return false
}
if lhs.deviceMetrics != rhs.deviceMetrics {
return false
}
if lhs.emojiContent != rhs.emojiContent {
return false
}
if lhs.stickerContent != rhs.stickerContent {
return false
}
if lhs.backgroundIconColor != rhs.backgroundIconColor {
return false
}
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.separatorColor != rhs.separatorColor {
return false
}
if (lhs.backspace == nil) != (rhs.backspace == nil) {
return false
}
return true
}
public final class View: UIView {
private let keyboardView: ComponentView<Empty>
private let keyboardClippingView: UIView
private let panelHostView: PagerExternalTopPanelContainer
private let panelBackgroundView: BlurredBackgroundView
private let panelSeparatorView: UIView
private let shadowView: UIImageView
private let cornersView: UIImageView
private let backspaceButton = ComponentView<Empty>()
private let backspaceBackgroundView: UIImageView
private var component: EmojiSelectionComponent?
private weak var state: EmptyComponentState?
private var isSearchActive: Bool = false
override init(frame: CGRect) {
self.keyboardView = ComponentView<Empty>()
self.keyboardClippingView = UIView()
self.panelHostView = PagerExternalTopPanelContainer()
self.panelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
self.panelSeparatorView = UIView()
self.shadowView = UIImageView()
self.cornersView = UIImageView()
self.backspaceBackgroundView = UIImageView()
super.init(frame: frame)
self.addSubview(self.keyboardClippingView)
self.addSubview(self.panelBackgroundView)
self.addSubview(self.panelSeparatorView)
self.addSubview(self.panelHostView)
self.addSubview(self.cornersView)
self.addSubview(self.shadowView)
self.shadowView.image = generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: CGSize(), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.05).cgColor)
context.setFillColor(UIColor.black.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size))
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size).insetBy(dx: -0.5, dy: -0.5))
})?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 16)
self.cornersView.image = generateImage(CGSize(width: 16.0 + 1.0, height: 16.0), rotatedContext: { size, context in
context.setFillColor(UIColor.white.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: size), cornerRadius: 8.0).cgPath)
context.fillPath()
context.clear(CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: 1.0, height: size.height)))
})?.withRenderingMode(.alwaysTemplate).stretchableImage(withLeftCapWidth: 8, topCapHeight: 16)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
public func internalRequestUpdate(transition: ComponentTransition) {
if let keyboardComponentView = self.keyboardView.view as? EntityKeyboardComponent.View {
keyboardComponentView.state?.updated(transition: transition)
}
}
func update(component: EmojiSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
self.backgroundColor = component.backgroundColor
let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85)
self.panelBackgroundView.updateColor(color: panelBackgroundColor, transition: .immediate)
self.panelSeparatorView.backgroundColor = component.separatorColor
let previousComponent = self.component
self.component = component
self.state = state
var resolvedHeight: CGFloat = min(340.0, max(50.0, availableSize.height - 200.0))
if self.isSearchActive {
resolvedHeight = min(availableSize.height, resolvedHeight + 200.0)
}
self.cornersView.tintColor = component.theme.list.blocksBackgroundColor
transition.setFrame(view: self.cornersView, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: availableSize.width, height: 16.0)))
transition.setFrame(view: self.shadowView, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: availableSize.width, height: 16.0)))
let topPanelHeight: CGFloat = 42.0
let backspaceButtonInset = UIEdgeInsets(top: 9.0, left: 0.0, bottom: 36.0, right: 9.0)
let backspaceButtonSize = CGSize(width: 36.0, height: 36.0)
let _ = self.backspaceButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(HStack([], spacing: 0.0)),
action: { [weak self] in
guard let self, let component = self.component else {
return
}
component.backspace?()
AudioServicesPlaySystemSound(1155)
}
).withHoldAction({ [weak self] _ in
guard let self, let component = self.component else {
return
}
AudioServicesPlaySystemSound(1155)
component.backspace?()
})),
environment: {},
containerSize: backspaceButtonSize
)
if previousComponent?.theme !== component.theme {
self.backspaceBackgroundView.image = generateImage(CGSize(width: backspaceButtonSize.width + 12.0 * 2.0, height: backspaceButtonSize.height + 12.0 * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: CGSize(), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.15).cgColor)
context.setFillColor(component.theme.list.plainBackgroundColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: backspaceButtonSize))
context.setShadow(offset: CGSize(), blur: 0.0, color: nil)
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputClearIcon"), color: component.theme.chat.inputMediaPanel.panelIconColor) {
let imageSize = image.size
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: 12.0 + floor((backspaceButtonSize.width - imageSize.width) * 0.5) - 1.0, y: 12.0 + floor((backspaceButtonSize.height - imageSize.height) * 0.5)), size: imageSize))
}
})
}
self.backspaceBackgroundView.frame = CGRect(origin: CGPoint(), size: backspaceButtonSize).insetBy(dx: -12.0, dy: -12.0)
let backspaceButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.sideInset - backspaceButtonInset.right - backspaceButtonSize.width, y: resolvedHeight - component.bottomInset - backspaceButtonInset.bottom), size: backspaceButtonSize)
if let backspaceButtonView = self.backspaceButton.view {
if backspaceButtonView.superview == nil {
backspaceButtonView.addSubview(self.backspaceBackgroundView)
self.addSubview(backspaceButtonView)
}
transition.setPosition(view: backspaceButtonView, position: backspaceButtonFrame.center)
transition.setBounds(view: backspaceButtonView, bounds: CGRect(origin: CGPoint(), size: backspaceButtonFrame.size))
if component.backspace != nil {
transition.setAlpha(view: backspaceButtonView, alpha: 1.0)
transition.setScale(view: backspaceButtonView, scale: 1.0)
} else {
transition.setAlpha(view: backspaceButtonView, alpha: 0.0)
transition.setScale(view: backspaceButtonView, scale: 0.001)
}
}
self.keyboardView.parentState = state
let keyboardSize = self.keyboardView.update(
transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
component: AnyComponent(EntityKeyboardComponent(
theme: component.theme,
strings: component.strings,
isContentInFocus: true,
containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: component.sideInset, bottom: component.bottomInset + 16.0, right: component.sideInset),
topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0),
emojiContent: component.emojiContent?.withCustomTintColor(component.theme.list.itemPrimaryTextColor),
stickerContent: component.stickerContent?.withCustomTintColor(component.theme.list.itemPrimaryTextColor),
maskContent: nil,
gifContent: nil,
hasRecentGifs: false,
availableGifSearchEmojies: [],
defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in },
hideInputUpdated: { _, _, _ in },
hideTopPanelUpdated: { [weak self] hideTopPanel, transition in
guard let self else {
return
}
if self.isSearchActive != hideTopPanel {
self.isSearchActive = hideTopPanel
self.state?.updated(transition: transition)
}
},
switchToTextInput: {},
switchToGifSubject: { _ in },
reorderItems: { _, _ in },
makeSearchContainerNode: { _ in return nil },
contentIdUpdated: { _ in },
deviceMetrics: component.deviceMetrics,
hiddenInputHeight: 0.0,
inputHeight: 0.0,
displayBottomPanel: false,
isExpanded: true,
clipContentToTopPanel: false,
useExternalSearchContainer: false,
customTintColor: component.backgroundIconColor
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: resolvedHeight)
)
if let keyboardComponentView = self.keyboardView.view {
if keyboardComponentView.superview == nil {
self.keyboardClippingView.addSubview(keyboardComponentView)
}
if panelBackgroundColor.alpha < 0.01 {
self.keyboardClippingView.clipsToBounds = true
} else {
self.keyboardClippingView.clipsToBounds = false
}
transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: availableSize.width, height: resolvedHeight - topPanelHeight)))
transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight), size: keyboardSize))
transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0)))
transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight)))
self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel)))
transition.setAlpha(view: self.panelSeparatorView, alpha: 1.0)
}
return CGSize(width: availableSize.width, height: resolvedHeight)
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}