Swiftgram/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntitySearchContentComponent.swift
2024-06-12 23:04:04 +04:00

181 lines
6.3 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import PagerComponent
import TelegramPresentationData
import TelegramCore
import Postbox
import AnimationCache
import MultiAnimationRenderer
import AccountContext
import AsyncDisplayKit
import ComponentDisplayAdapters
public protocol EntitySearchContainerNode: ASDisplayNode {
var onCancel: (() -> Void)? { get set }
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition)
}
public final class EntitySearchContainerController: ViewController {
private var node: Node {
return self.displayNode as! Node
}
private let containerNode: EntitySearchContainerNode
public init(containerNode: EntitySearchContainerNode) {
self.containerNode = containerNode
super.init(navigationBarPresentationData: nil)
self.navigationPresentation = .modal
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = Node(containerNode: self.containerNode, controller: self)
self.displayNodeDidLoad()
}
public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.node.containerLayoutUpdated(layout, transition: transition)
}
private class Node: ViewControllerTracingNode, ASScrollViewDelegate {
private weak var controller: EntitySearchContainerController?
private let containerNode: EntitySearchContainerNode
init(containerNode: EntitySearchContainerNode, controller: EntitySearchContainerController) {
self.containerNode = containerNode
self.controller = controller
super.init()
self.addSubnode(containerNode)
containerNode.onCancel = { [weak self] in
self?.controller?.dismiss()
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.containerNode.updateLayout(size: layout.size, leftInset: 0.0, rightInset: 0.0, bottomInset: layout.intrinsicInsets.bottom, inputHeight: layout.inputHeight ?? 0.0, deviceMetrics: layout.deviceMetrics, transition: transition)
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: .zero, size: layout.size))
}
}
}
final class EntitySearchContentEnvironment: Equatable {
let context: AccountContext
let theme: PresentationTheme
let deviceMetrics: DeviceMetrics
let inputHeight: CGFloat
init(
context: AccountContext,
theme: PresentationTheme,
deviceMetrics: DeviceMetrics,
inputHeight: CGFloat
) {
self.context = context
self.theme = theme
self.deviceMetrics = deviceMetrics
self.inputHeight = inputHeight
}
static func ==(lhs: EntitySearchContentEnvironment, rhs: EntitySearchContentEnvironment) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.deviceMetrics != rhs.deviceMetrics {
return false
}
if lhs.inputHeight != rhs.inputHeight {
return false
}
return true
}
}
final class EntitySearchContentComponent: Component {
typealias EnvironmentType = EntitySearchContentEnvironment
let makeContainerNode: () -> EntitySearchContainerNode?
let dismissSearch: () -> Void
init(
makeContainerNode: @escaping () -> EntitySearchContainerNode?,
dismissSearch: @escaping () -> Void
) {
self.makeContainerNode = makeContainerNode
self.dismissSearch = dismissSearch
}
static func ==(lhs: EntitySearchContentComponent, rhs: EntitySearchContentComponent) -> Bool {
return true
}
final class View: UIView {
private var containerNode: EntitySearchContainerNode?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: EntitySearchContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
let containerNode: EntitySearchContainerNode?
if let current = self.containerNode {
containerNode = current
} else {
containerNode = component.makeContainerNode()
if let containerNode = containerNode {
self.containerNode = containerNode
self.addSubnode(containerNode)
}
}
if let containerNode = containerNode {
let environmentValue = environment[EntitySearchContentEnvironment.self].value
transition.setFrame(view: containerNode.view, frame: CGRect(origin: CGPoint(), size: availableSize))
containerNode.updateLayout(
size: availableSize,
leftInset: 0.0,
rightInset: 0.0,
bottomInset: 0.0,
inputHeight: environmentValue.inputHeight,
deviceMetrics: environmentValue.deviceMetrics,
transition: transition.containedViewLayoutTransition
)
containerNode.onCancel = {
component.dismissSearch()
}
}
return availableSize
}
}
func makeView() -> View {
return View(frame: CGRect())
}
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)
}
}