mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Initial implementation of channel overscroll navigation
This commit is contained in:
203
submodules/ComponentFlow/Source/Base/Component.swift
Normal file
203
submodules/ComponentFlow/Source/Base/Component.swift
Normal file
@@ -0,0 +1,203 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import ObjectiveC
|
||||
|
||||
public class ComponentLayoutResult {
|
||||
var availableSize: CGSize?
|
||||
var size: CGSize?
|
||||
}
|
||||
|
||||
public protocol _TypeErasedComponentContext: AnyObject {
|
||||
var erasedEnvironment: _Environment { get }
|
||||
var erasedState: ComponentState { get }
|
||||
|
||||
var layoutResult: ComponentLayoutResult { get }
|
||||
}
|
||||
|
||||
class AnyComponentContext<EnvironmentType>: _TypeErasedComponentContext {
|
||||
var erasedComponent: AnyComponent<EnvironmentType> {
|
||||
get {
|
||||
preconditionFailure()
|
||||
} set(value) {
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
var erasedState: ComponentState {
|
||||
preconditionFailure()
|
||||
}
|
||||
var erasedEnvironment: _Environment {
|
||||
return self.environment
|
||||
}
|
||||
|
||||
let layoutResult: ComponentLayoutResult
|
||||
var environment: Environment<EnvironmentType>
|
||||
|
||||
init(environment: Environment<EnvironmentType>) {
|
||||
self.layoutResult = ComponentLayoutResult()
|
||||
self.environment = environment
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentContext<ComponentType: Component>: AnyComponentContext<ComponentType.EnvironmentType> {
|
||||
override var erasedComponent: AnyComponent<ComponentType.EnvironmentType> {
|
||||
get {
|
||||
return AnyComponent(self.component)
|
||||
} set(value) {
|
||||
self.component = value.wrapped as! ComponentType
|
||||
}
|
||||
}
|
||||
|
||||
var component: ComponentType
|
||||
let state: ComponentType.State
|
||||
|
||||
override var erasedState: ComponentState {
|
||||
return self.state
|
||||
}
|
||||
|
||||
init(component: ComponentType, environment: Environment<ComponentType.EnvironmentType>, state: ComponentType.State) {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
super.init(environment: environment)
|
||||
}
|
||||
}
|
||||
|
||||
private var UIView_TypeErasedComponentContextKey: Int?
|
||||
|
||||
extension UIView {
|
||||
func context<EnvironmentType>(component: AnyComponent<EnvironmentType>) -> AnyComponentContext<EnvironmentType> {
|
||||
return self.context(typeErasedComponent: component) as! AnyComponentContext<EnvironmentType>
|
||||
}
|
||||
|
||||
func context<ComponentType: Component>(component: ComponentType) -> ComponentContext<ComponentType> {
|
||||
return self.context(typeErasedComponent: component) as! ComponentContext<ComponentType>
|
||||
}
|
||||
|
||||
func context(typeErasedComponent component: _TypeErasedComponent) -> _TypeErasedComponentContext{
|
||||
if let context = objc_getAssociatedObject(self, &UIView_TypeErasedComponentContextKey) as? _TypeErasedComponentContext {
|
||||
return context
|
||||
} else {
|
||||
let context = component._makeContext()
|
||||
objc_setAssociatedObject(self, &UIView_TypeErasedComponentContextKey, context, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
return context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ComponentState {
|
||||
var _updated: ((Transition) -> Void)?
|
||||
var isUpdated: Bool = false
|
||||
|
||||
public final func updated(transition: Transition = .immediate) {
|
||||
self.isUpdated = true
|
||||
self._updated?(transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class EmptyComponentState: ComponentState {
|
||||
}
|
||||
|
||||
public protocol _TypeErasedComponent {
|
||||
func _makeView() -> UIView
|
||||
func _makeContext() -> _TypeErasedComponentContext
|
||||
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize
|
||||
func _isEqual(to other: _TypeErasedComponent) -> Bool
|
||||
}
|
||||
|
||||
public protocol Component: _TypeErasedComponent, Equatable {
|
||||
associatedtype EnvironmentType = Empty
|
||||
associatedtype View: UIView = UIView
|
||||
associatedtype State: ComponentState = EmptyComponentState
|
||||
|
||||
func makeView() -> View
|
||||
func makeState() -> State
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize
|
||||
}
|
||||
|
||||
public extension Component {
|
||||
func _makeView() -> UIView {
|
||||
return self.makeView()
|
||||
}
|
||||
|
||||
func _makeContext() -> _TypeErasedComponentContext {
|
||||
return ComponentContext<Self>(component: self, environment: Environment<EnvironmentType>(), state: self.makeState())
|
||||
}
|
||||
|
||||
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return self.update(view: view as! Self.View, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
|
||||
func _isEqual(to other: _TypeErasedComponent) -> Bool {
|
||||
if let other = other as? Self {
|
||||
return self == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Component where Self.View == UIView {
|
||||
func makeView() -> UIView {
|
||||
return UIView()
|
||||
}
|
||||
}
|
||||
|
||||
public extension Component where Self.State == EmptyComponentState {
|
||||
func makeState() -> State {
|
||||
return EmptyComponentState()
|
||||
}
|
||||
}
|
||||
|
||||
public class ComponentGesture {
|
||||
public static func tap(action: @escaping() -> Void) -> ComponentGesture {
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
public class AnyComponent<EnvironmentType>: _TypeErasedComponent, Equatable {
|
||||
fileprivate let wrapped: _TypeErasedComponent
|
||||
|
||||
public init<ComponentType: Component>(_ component: ComponentType) where ComponentType.EnvironmentType == EnvironmentType {
|
||||
self.wrapped = component
|
||||
}
|
||||
|
||||
public static func ==(lhs: AnyComponent<EnvironmentType>, rhs: AnyComponent<EnvironmentType>) -> Bool {
|
||||
return lhs.wrapped._isEqual(to: rhs.wrapped)
|
||||
}
|
||||
|
||||
public func _makeView() -> UIView {
|
||||
return self.wrapped._makeView()
|
||||
}
|
||||
|
||||
public func _makeContext() -> _TypeErasedComponentContext {
|
||||
return self.wrapped._makeContext()
|
||||
}
|
||||
|
||||
public func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return self.wrapped._update(view: view, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
|
||||
public func _isEqual(to other: _TypeErasedComponent) -> Bool {
|
||||
return self.wrapped._isEqual(to: other)
|
||||
}
|
||||
}
|
||||
|
||||
public final class AnyComponentWithIdentity<Environment>: Equatable {
|
||||
public let id: AnyHashable
|
||||
public let component: AnyComponent<Environment>
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, component: AnyComponent<Environment>) {
|
||||
self.id = AnyHashable(id)
|
||||
self.component = component
|
||||
}
|
||||
|
||||
public static func == (lhs: AnyComponentWithIdentity<Environment>, rhs: AnyComponentWithIdentity<Environment>) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user