Swiftgram/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift
2022-07-02 00:04:43 +02:00

184 lines
8.2 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import PagerComponent
import TelegramPresentationData
import TelegramCore
import Postbox
final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
let visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
init(
visibilityFractionUpdated: ActionSlot<(CGFloat, Transition)>
) {
self.visibilityFractionUpdated = visibilityFractionUpdated
}
static func ==(lhs: EntityKeyboardTopContainerPanelEnvironment, rhs: EntityKeyboardTopContainerPanelEnvironment) -> Bool {
if lhs.visibilityFractionUpdated !== rhs.visibilityFractionUpdated {
return false
}
return true
}
}
final class EntityKeyboardTopContainerPanelComponent: Component {
typealias EnvironmentType = PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>
let theme: PresentationTheme
init(
theme: PresentationTheme
) {
self.theme = theme
}
static func ==(lhs: EntityKeyboardTopContainerPanelComponent, rhs: EntityKeyboardTopContainerPanelComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
return true
}
private final class PanelView {
let view = ComponentHostView<EntityKeyboardTopContainerPanelEnvironment>()
let visibilityFractionUpdated = ActionSlot<(CGFloat, Transition)>()
}
final class View: UIView {
private var panelViews: [AnyHashable: PanelView] = [:]
private var component: EntityKeyboardTopContainerPanelComponent?
private var panelEnvironment: PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>?
private weak var state: EmptyComponentState?
private var visibilityFraction: CGFloat = 1.0
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: EntityKeyboardTopContainerPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let intrinsicHeight: CGFloat = 41.0
let height = intrinsicHeight
let panelEnvironment = environment[PagerComponentPanelEnvironment.self].value
var transitionOffsetFraction: CGFloat = 0.0
if case .none = transition.animation {
} else if let previousPanelEnvironment = self.panelEnvironment, let previousActiveContentId = previousPanelEnvironment.activeContentId, let activeContentId = panelEnvironment.activeContentId, previousActiveContentId != activeContentId {
if let previousIndex = panelEnvironment.contentTopPanels.firstIndex(where: { $0.id == previousActiveContentId }), let index = panelEnvironment.contentTopPanels.firstIndex(where: { $0.id == activeContentId }), previousIndex != index {
if index < previousIndex {
transitionOffsetFraction = -1.0
} else {
transitionOffsetFraction = 1.0
}
}
}
self.component = component
self.panelEnvironment = panelEnvironment
self.state = state
var validPanelIds = Set<AnyHashable>()
let visibleBounds = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: intrinsicHeight))
if let centralId = panelEnvironment.activeContentId, let centralIndex = panelEnvironment.contentTopPanels.firstIndex(where: { $0.id == centralId }) {
for index in 0 ..< panelEnvironment.contentTopPanels.count {
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 isInBounds = visibleBounds.intersects(panelFrame)
let isPartOfTransition: Bool
if !transitionOffsetFraction.isZero && self.panelViews[panel.id] != nil {
isPartOfTransition = true
} else {
isPartOfTransition = false
}
if isInBounds || isPartOfTransition {
validPanelIds.insert(panel.id)
var panelTransition = transition
let panelView: PanelView
if let current = self.panelViews[panel.id] {
panelView = current
} else {
panelTransition = .immediate
panelView = PanelView()
self.panelViews[panel.id] = panelView
self.addSubview(panelView.view)
}
let _ = panelView.view.update(
transition: panelTransition,
component: panel.component,
environment: {
EntityKeyboardTopContainerPanelEnvironment(
visibilityFractionUpdated: panelView.visibilityFractionUpdated
)
},
containerSize: panelFrame.size
)
if isInBounds {
transition.animatePosition(view: panelView.view, from: CGPoint(x: transitionOffsetFraction * availableSize.width, y: 0.0), to: CGPoint(), additive: true, completion: nil)
}
panelTransition.setFrame(view: panelView.view, frame: panelFrame, completion: { [weak self] completed in
if isPartOfTransition && completed {
self?.state?.updated(transition: .immediate)
}
})
}
}
}
var removedPanelIds: [AnyHashable] = []
for (id, panelView) in self.panelViews {
if !validPanelIds.contains(id) {
removedPanelIds.append(id)
panelView.view.removeFromSuperview()
}
}
for id in removedPanelIds {
self.panelViews.removeValue(forKey: id)
}
environment[PagerComponentPanelEnvironment.self].value.visibilityFractionUpdated.connect { [weak self] (fraction, transition) in
guard let strongSelf = self else {
return
}
strongSelf.updateVisibilityFraction(value: fraction, transition: transition)
}
return CGSize(width: availableSize.width, height: height)
}
private func updateVisibilityFraction(value: CGFloat, transition: Transition) {
if self.visibilityFraction == value {
return
}
self.visibilityFraction = value
for (_, panelView) in self.panelViews {
panelView.visibilityFractionUpdated.invoke((value, transition))
transition.setSublayerTransform(view: panelView.view, transform: CATransform3DMakeTranslation(0.0, -panelView.view.bounds.height / 2.0 * (1.0 - value), 0.0))
}
}
}
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)
}
}