import Foundation import UIKit import Display import AsyncDisplayKit import Postbox import TelegramCore import SwiftSignalKit import AccountContext import TelegramPresentationData import PresentationDataUtils import ComponentFlow import ViewControllerComponent import MultilineTextComponent import BundleIconComponent private final class PremimLimitsListScreenComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext let expand: () -> Void init(context: AccountContext, expand: @escaping () -> Void) { self.context = context self.expand = expand } static func ==(lhs: PremimLimitsListScreenComponent, rhs: PremimLimitsListScreenComponent) -> Bool { if lhs.context !== rhs.context { return false } return true } final class State: ComponentState { private let context: AccountContext init(context: AccountContext) { self.context = context super.init() } } func makeState() -> State { return State(context: self.context) } static var body: Body { return { context in // let environment = context.environment[ViewControllerComponentContainer.Environment.self].value // let state = context.state // let theme = environment.theme // let strings = environment.strings // return CGSize(width: context.availableSize.width, height: environment.navigationHeight + image.size.height + environment.safeInsets.bottom) return context.availableSize } } } public class PremimLimitsListScreen: ViewController { final class Node: ViewControllerTracingNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { private var presentationData: PresentationData private weak var controller: PremimLimitsListScreen? private let component: AnyComponent private let theme: PresentationTheme? let dim: ASDisplayNode let wrappingView: UIView let containerView: UIView let scrollView: UIScrollView let hostView: ComponentHostView private(set) var isExpanded = false private var panGestureRecognizer: UIPanGestureRecognizer? private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)? private var currentIsVisible: Bool = false private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? fileprivate var temporaryDismiss = false init(context: AccountContext, controller: PremimLimitsListScreen, component: AnyComponent, theme: PresentationTheme?) { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.controller = controller self.component = component self.theme = theme self.dim = ASDisplayNode() self.dim.alpha = 0.0 self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25) self.wrappingView = UIView() self.containerView = UIView() self.scrollView = UIScrollView() self.hostView = ComponentHostView() super.init() self.scrollView.delegate = self self.scrollView.showsVerticalScrollIndicator = false self.containerView.clipsToBounds = true self.containerView.backgroundColor = self.presentationData.theme.list.plainBackgroundColor self.addSubnode(self.dim) self.view.addSubview(self.wrappingView) self.wrappingView.addSubview(self.containerView) self.containerView.addSubview(self.scrollView) self.scrollView.addSubview(self.hostView) } override func didLoad() { super.didLoad() let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) panRecognizer.delegate = self panRecognizer.delaysTouchesBegan = false panRecognizer.cancelsTouchesInView = true self.panGestureRecognizer = panRecognizer self.wrappingView.addGestureRecognizer(panRecognizer) self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { self.controller?.dismiss(animated: true) } } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let (layout, _) = self.currentLayout { if case .regular = layout.metrics.widthClass { return false } } return true } func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentOffset = self.scrollView.contentOffset.y self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer { return true } return false } private var isDismissing = false func animateIn() { ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear).updateAlpha(node: self.dim, alpha: 1.0) let targetPosition = self.containerView.center let startPosition = targetPosition.offsetBy(dx: 0.0, dy: self.bounds.height) self.containerView.center = startPosition let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) transition.animateView(allowUserInteraction: true, { self.containerView.center = targetPosition }, completion: { _ in }) } func animateOut(completion: @escaping () -> Void = {}) { self.isDismissing = true let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) positionTransition.updatePosition(layer: self.containerView.layer, position: CGPoint(x: self.containerView.center.x, y: self.bounds.height + self.containerView.bounds.height / 2.0), completion: { [weak self] _ in self?.controller?.dismiss(animated: false, completion: completion) }) let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) alphaTransition.updateAlpha(node: self.dim, alpha: 0.0) if !self.temporaryDismiss { self.controller?.updateModalStyleOverlayTransitionFactor(0.0, transition: positionTransition) } } func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) { self.currentLayout = (layout, navigationHeight) if let controller = self.controller, let navigationBar = controller.navigationBar, navigationBar.view.superview !== self.wrappingView { self.containerView.addSubview(navigationBar.view) } self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0)) var effectiveExpanded = self.isExpanded if case .regular = layout.metrics.widthClass { effectiveExpanded = true } let isLandscape = layout.orientation == .landscape let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset let topInset: CGFloat if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments { if effectiveExpanded { topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset)) } else { topInset = max(0.0, panInitialTopInset + min(0.0, panOffset)) } } else { topInset = effectiveExpanded ? 0.0 : edgeTopInset } transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: nil) let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset) self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition) let clipFrame: CGRect if layout.metrics.widthClass == .compact { self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25) if isLandscape { self.containerView.layer.cornerRadius = 0.0 } else { self.containerView.layer.cornerRadius = 10.0 } if #available(iOS 11.0, *) { if layout.safeInsets.bottom.isZero { self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] } else { self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner] } } if isLandscape { clipFrame = CGRect(origin: CGPoint(), size: layout.size) } else { let coveredByModalTransition: CGFloat = 0.0 var containerTopInset: CGFloat = 10.0 if let statusBarHeight = layout.statusBarHeight { containerTopInset += statusBarHeight } let unscaledFrame = CGRect(origin: CGPoint(x: 0.0, y: containerTopInset - coveredByModalTransition * 10.0), size: CGSize(width: layout.size.width, height: layout.size.height - containerTopInset)) let maxScale: CGFloat = (layout.size.width - 16.0 * 2.0) / layout.size.width let containerScale = 1.0 * (1.0 - coveredByModalTransition) + maxScale * coveredByModalTransition let maxScaledTopInset: CGFloat = containerTopInset - 10.0 let scaledTopInset: CGFloat = containerTopInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition let containerFrame = unscaledFrame.offsetBy(dx: 0.0, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0)) clipFrame = CGRect(x: containerFrame.minX, y: containerFrame.minY, width: containerFrame.width, height: containerFrame.height) } } else { self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.4) self.containerView.layer.cornerRadius = 10.0 let verticalInset: CGFloat = 44.0 let maxSide = max(layout.size.width, layout.size.height) let minSide = min(layout.size.width, layout.size.height) let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) } transition.setFrame(view: self.containerView, frame: clipFrame) transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: clipFrame.size), completion: nil) let environment = ViewControllerComponentContainer.Environment( statusBarHeight: 0.0, navigationHeight: navigationHeight, safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), isVisible: self.currentIsVisible, theme: self.theme ?? self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in return self?.controller } ) var contentSize = self.hostView.update( transition: transition, component: self.component, environment: { environment }, forceUpdate: true, containerSize: CGSize(width: clipFrame.size.width, height: 10000.0) ) contentSize.height = max(layout.size.height - navigationHeight, contentSize.height) transition.setFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil) self.scrollView.contentSize = contentSize } private var didPlayAppearAnimation = false func updateIsVisible(isVisible: Bool) { if self.currentIsVisible == isVisible { return } self.currentIsVisible = isVisible guard let currentLayout = self.currentLayout else { return } self.containerLayoutUpdated(layout: currentLayout.layout, navigationHeight: currentLayout.navigationHeight, transition: .immediate) if !self.didPlayAppearAnimation { self.didPlayAppearAnimation = true self.animateIn() } } private var defaultTopInset: CGFloat { guard let (layout, _) = self.currentLayout else{ return 210.0 } if case .compact = layout.metrics.widthClass { var factor: CGFloat = 0.2488 if layout.size.width <= 320.0 { factor = 0.15 } return floor(max(layout.size.width, layout.size.height) * factor) } else { return 210.0 } } private func findScrollView(view: UIView?) -> (UIScrollView, ListView?)? { if let view = view { if let view = view as? UIScrollView { return (view, nil) } if let node = view.asyncdisplaykit_node as? ListView { return (node.scroller, node) } return findScrollView(view: view.superview) } else { return nil } } @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { guard let (layout, navigationHeight) = self.currentLayout else { return } let isLandscape = layout.orientation == .landscape let edgeTopInset = isLandscape ? 0.0 : defaultTopInset switch recognizer.state { case .began: let point = recognizer.location(in: self.view) let currentHitView = self.hitTest(point, with: nil) var scrollViewAndListNode = self.findScrollView(view: currentHitView) if scrollViewAndListNode?.0.frame.height == self.frame.width { scrollViewAndListNode = nil } let scrollView = scrollViewAndListNode?.0 let listNode = scrollViewAndListNode?.1 let topInset: CGFloat if self.isExpanded { topInset = 0.0 } else { topInset = edgeTopInset } self.panGestureArguments = (topInset, 0.0, scrollView, listNode) case .changed: guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else { return } let visibleContentOffset = listNode?.visibleContentOffset() let contentOffset = scrollView?.contentOffset.y ?? 0.0 var translation = recognizer.translation(in: self.view).y var currentOffset = topInset + translation let epsilon = 1.0 if case let .known(value) = visibleContentOffset, value <= epsilon { if let scrollView = scrollView { scrollView.bounces = false scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false) } } else if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon { scrollView.bounces = false scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) } else if let scrollView = scrollView { translation = panOffset currentOffset = topInset + translation if self.isExpanded { recognizer.setTranslation(CGPoint(), in: self.view) } else if currentOffset > 0.0 { scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) } } self.panGestureArguments = (topInset, translation, scrollView, listNode) if !self.isExpanded { if currentOffset > 0.0, let scrollView = scrollView { scrollView.panGestureRecognizer.setTranslation(CGPoint(), in: scrollView) } } var bounds = self.bounds if self.isExpanded { bounds.origin.y = -max(0.0, translation - edgeTopInset) } else { bounds.origin.y = -translation } bounds.origin.y = min(0.0, bounds.origin.y) self.bounds = bounds self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate) case .ended: guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else { return } self.panGestureArguments = nil let visibleContentOffset = listNode?.visibleContentOffset() let contentOffset = scrollView?.contentOffset.y ?? 0.0 let translation = recognizer.translation(in: self.view).y var velocity = recognizer.velocity(in: self.view) if self.isExpanded { if case let .known(value) = visibleContentOffset, value > 0.1 { velocity = CGPoint() } else if case .unknown = visibleContentOffset { velocity = CGPoint() } else if contentOffset > 0.1 { velocity = CGPoint() } } var bounds = self.bounds if self.isExpanded { bounds.origin.y = -max(0.0, translation - edgeTopInset) } else { bounds.origin.y = -translation } bounds.origin.y = min(0.0, bounds.origin.y) scrollView?.bounces = true let offset = currentTopInset + panOffset let topInset: CGFloat = edgeTopInset var dismissing = false if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0) { self.controller?.dismiss(animated: true, completion: nil) dismissing = true } else if self.isExpanded { if velocity.y > 300.0 || offset > topInset / 2.0 { self.isExpanded = false if let listNode = listNode { listNode.scroller.setContentOffset(CGPoint(), animated: false) } else if let scrollView = scrollView { scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) } let distance = topInset - offset let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance) let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } else { self.isExpanded = true self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) } } else if (velocity.y < -300.0 || offset < topInset / 2.0) { if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode { DispatchQueue.main.async { listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } } let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset) let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) self.isExpanded = true self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } else { if let listNode = listNode { listNode.scroller.setContentOffset(CGPoint(), animated: false) } else if let scrollView = scrollView { scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) } self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) } if !dismissing { var bounds = self.bounds let previousBounds = bounds bounds.origin.y = 0.0 self.bounds = bounds self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) } case .cancelled: self.panGestureArguments = nil self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) default: break } } func update(isExpanded: Bool, transition: ContainedViewLayoutTransition) { guard isExpanded != self.isExpanded else { return } self.isExpanded = isExpanded guard let (layout, navigationHeight) = self.currentLayout else { return } self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } } var node: Node { return self.displayNode as! Node } private let context: AccountContext private let theme: PresentationTheme? private let component: AnyComponent private var isInitiallyExpanded = false private var currentLayout: ContainerViewLayout? public convenience init(context: AccountContext) { var expandImpl: (() -> Void)? self.init(context: context, component: PremimLimitsListScreenComponent(context: context, expand: { expandImpl?() })) self.title = "Doubled Limits" self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) let rightBarButtonNode = ASImageNode() rightBarButtonNode.image = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xededed), foregroundColor: UIColor(rgb: 0x7f8084)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: rightBarButtonNode) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) expandImpl = { [weak self] in self?.node.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring)) if let currentLayout = self?.currentLayout { self?.containerLayoutUpdated(currentLayout, transition: .animated(duration: 0.4, curve: .spring)) } } } private init(context: AccountContext, component: C, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment { self.context = context self.component = AnyComponent(component) self.theme = nil super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 })) self.navigationPresentation = .flatModal } required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func cancelPressed() { self.dismiss(animated: true, completion: nil) } override open func loadDisplayNode() { self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme) if self.isInitiallyExpanded { (self.displayNode as! Node).update(isExpanded: true, transition: .immediate) } self.displayNodeDidLoad() self.view.disablesInteractiveModalDismiss = true } public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { self.view.endEditing(true) if flag { self.node.animateOut(completion: { super.dismiss(animated: false, completion: {}) completion?() }) } else { super.dismiss(animated: false, completion: {}) completion?() } } override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.node.updateIsVisible(isVisible: true) } override open func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.node.updateIsVisible(isVisible: false) } override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { var navigationLayout = self.navigationLayout(layout: layout) var navigationFrame = navigationLayout.navigationFrame var layout = layout if case .regular = layout.metrics.widthClass { let verticalInset: CGFloat = 44.0 let maxSide = max(layout.size.width, layout.size.height) let minSide = min(layout.size.width, layout.size.height) let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) let clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) navigationFrame.size.width = clipFrame.width layout.size = clipFrame.size } navigationFrame.size.height = 56.0 navigationLayout.navigationFrame = navigationFrame navigationLayout.defaultContentHeight = 56.0 layout.statusBarHeight = nil self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition) } override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.currentLayout = layout super.containerLayoutUpdated(layout, transition: transition) let navigationHeight: CGFloat = 56.0 self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } } //import Foundation //import UIKit //import Display //import AsyncDisplayKit //import Postbox //import TelegramCore //import SwiftSignalKit //import AccountContext //import TelegramPresentationData //import PresentationDataUtils //import ComponentFlow //import ViewControllerComponent //import SheetComponent //import MultilineTextComponent //import BundleIconComponent //import SolidRoundedButtonComponent //import Markdown // //private final class PremiumLimitsListContent: CombinedComponent { // typealias EnvironmentType = ViewControllerComponentContainer.Environment // // let context: AccountContext // let subject: PremiumDemoScreen.Subject // let source: PremiumDemoScreen.Source // let action: () -> Void // let dismiss: () -> Void // // init(context: AccountContext, action: @escaping () -> Void, dismiss: @escaping () -> Void) { // self.context = context // self.action = action // self.dismiss = dismiss // } // // static func ==(lhs: PremiumLimitsListContent, rhs: PremiumLimitsListContent) -> Bool { // if lhs.context !== rhs.context { // return false // } // return true // } // // final class State: ComponentState { // private let context: AccountContext // var cachedCloseImage: UIImage? // // var limits: EngineConfiguration.UserLimits = .defaultValue // var premiumLimits: EngineConfiguration.UserLimits = .defaultValue // var disposable: Disposable? // // init(context: AccountContext) { // self.context = context // // super.init() // // self.disposable = (self.context.engine.data.get( // TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), // TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) // ) // |> deliverOnMainQueue).start(next: { [weak self] limits, premiumLimits in // guard let strongSelf = self else { // return // } // strongSelf.limits = limits // strongSelf.premiumLimits = premiumLimits // strongSelf.updated(transition: .immediate) // }) // } // // deinit { // self.disposable?.dispose() // } // } // // func makeState() -> State { // return State(context: self.context) // } // // static var body: Body { // let closeButton = Child(Button.self) // let button = Child(SolidRoundedButtonComponent.self) // // return { context in // let environment = context.environment[ViewControllerComponentContainer.Environment.self].value // let component = context.component //// let theme = environment.theme // let strings = environment.strings // // let state = context.state // // let sideInset: CGFloat = 16.0 + environment.safeInsets.left // // let closeImage: UIImage // if let image = state.cachedCloseImage { // closeImage = image // } else { // closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: UIColor(rgb: 0xffffff))! // state.cachedCloseImage = closeImage // } // // let closeButton = closeButton.update( // component: Button( // content: AnyComponent(Image(image: closeImage)), // action: { [weak component] in // component?.dismiss() // } // ), // availableSize: CGSize(width: 30.0, height: 30.0), // transition: .immediate // ) // context.add(closeButton // .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0)) // ) // // let buttonText: String // switch component.source { // case let .intro(price): // buttonText = strings.Premium_SubscribeFor(price ?? "–").string // case .other: // buttonText = strings.Premium_MoreAboutPremium // } // // let button = button.update( // component: SolidRoundedButtonComponent( // title: buttonText, // theme: SolidRoundedButtonComponent.Theme( // backgroundColor: .black, // backgroundColors: [ // UIColor(rgb: 0x0077ff), // UIColor(rgb: 0x6b93ff), // UIColor(rgb: 0x8878ff), // UIColor(rgb: 0xe46ace) // ], // foregroundColor: .white // ), // font: .bold, // fontSize: 17.0, // height: 50.0, // cornerRadius: 10.0, // gloss: true, // iconPosition: .right, // action: { [weak component] in // guard let component = component else { // return // } // component.dismiss() // component.action() // } // ), // availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), // transition: context.transition // ) // // let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: context.availableSize.width + 154.0 + 20.0), size: button.size) // context.add(button // .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) // ) // // let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom) // // return contentSize // } // } //} // // //private final class PremiumLimitsListComponent: CombinedComponent { // typealias EnvironmentType = ViewControllerComponentContainer.Environment // // let context: AccountContext // let action: () -> Void // // init(context: AccountContext, action: @escaping () -> Void) { // self.context = context // self.action = action // } // // static func ==(lhs: PremiumLimitsListComponent, rhs: PremiumLimitsListComponent) -> Bool { // if lhs.context !== rhs.context { // return false // } // // return true // } // // static var body: Body { // let sheet = Child(SheetComponent.self) // let animateOut = StoredActionSlot(Action.self) // // return { context in // let environment = context.environment[EnvironmentType.self] // // let controller = environment.controller // // let sheet = sheet.update( // component: SheetComponent( // content: AnyComponent(PremiumLimitsListContent( // context: context.component.context, // action: context.component.action, // dismiss: { // animateOut.invoke(Action { _ in // if let controller = controller() { // controller.dismiss(completion: nil) // } // }) // } // )), // backgroundColor: environment.theme.actionSheet.opaqueItemBackgroundColor, // animateOut: animateOut // ), // environment: { // environment // SheetComponentEnvironment( // isDisplaying: environment.value.isVisible, // dismiss: { // animateOut.invoke(Action { _ in // if let controller = controller() { // controller.dismiss(completion: nil) // } // }) // } // ) // }, // availableSize: context.availableSize, // transition: context.transition // ) // // context.add(sheet // .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) // ) // // return context.availableSize // } // } //} // //public class PremiumLimitsListScreen: ViewControllerComponentContainer { // var disposed: () -> Void = {} // // public init(context: AccountContext, action: @escaping () -> Void) { // super.init(context: context, component: PremiumLimitsListComponent(context: context, action: action), navigationBarAppearance: .none) // // self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) // // self.navigationPresentation = .flatModal // } // // required public init(coder aDecoder: NSCoder) { // fatalError("init(coder:) has not been implemented") // } // // deinit { // self.disposed() // } // // public override func viewDidLoad() { // super.viewDidLoad() // // self.view.disablesInteractiveModalDismiss = true // } //} // // // // // //public final class ExpandingSheetEnvironment: Equatable { // public let isDisplaying: Bool // public let dismiss: () -> Void // // public init(isDisplaying: Bool, dismiss: @escaping () -> Void) { // self.isDisplaying = isDisplaying // self.dismiss = dismiss // } // // public static func ==(lhs: ExpandingSheetEnvironment, rhs: ExpandingSheetEnvironment) -> Bool { // if lhs.isDisplaying != rhs.isDisplaying { // return false // } // return true // } //} // //public final class ExpandingSheetComponent: Component { // public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment) // // public let content: AnyComponent // public let backgroundColor: UIColor // public let animateOut: ActionSlot> // // public init(content: AnyComponent, backgroundColor: UIColor, animateOut: ActionSlot>) { // self.content = content // self.backgroundColor = backgroundColor // self.animateOut = animateOut // } // // public static func ==(lhs: ExpandingSheetComponent, rhs: ExpandingSheetComponent) -> Bool { // if lhs.content != rhs.content { // return false // } // if lhs.backgroundColor != rhs.backgroundColor { // return false // } // if lhs.animateOut != rhs.animateOut { // return false // } // // return true // } // // public final class View: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate { // private let dimView: UIView // private let wrappingView: UIView // private let containerView: UIView // private let scrollView: UIScrollView // private let contentView: ComponentHostView // // private(set) var isExpanded = false // private var panGestureRecognizer: UIPanGestureRecognizer? // private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)? // // private var previousIsDisplaying: Bool = false // private var dismiss: (() -> Void)? // // override init(frame: CGRect) { // self.dimView = UIView() // self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.4) // // self.wrappingView = UIView() // self.containerView = UIView() // self.scrollView = UIScrollView() // if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { // self.scrollView.contentInsetAdjustmentBehavior = .never // } // // self.contentView = ComponentHostView() // // super.init(frame: frame) // // self.addSubview(self.dimView) // // self.scrollView.delegate = self // self.scrollView.showsVerticalScrollIndicator = false // // self.containerView.clipsToBounds = true // // self.addSubview(self.wrappingView) // self.wrappingView.addSubview(self.containerView) // self.containerView.addSubview(self.scrollView) // self.scrollView.addSubview(self.contentView) // // self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimViewTapGesture(_:)))) // // let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) // panRecognizer.delegate = self // panRecognizer.delaysTouchesBegan = false // panRecognizer.cancelsTouchesInView = true // self.panGestureRecognizer = panRecognizer // self.wrappingView.addGestureRecognizer(panRecognizer) // } // // required init?(coder: NSCoder) { // fatalError("init(coder:) has not been implemented") // } // // @objc private func dimViewTapGesture(_ recognizer: UITapGestureRecognizer) { // if case .ended = recognizer.state { // self.dismiss?() // } // } // // override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // if let (layout, _) = self.currentLayout { // if case .regular = layout.metrics.widthClass { // return false // } // } // return true // } // // func scrollViewDidScroll(_ scrollView: UIScrollView) { // let contentOffset = self.scrollView.contentOffset.y // self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) // } // // func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer { // return true // } // return false // } // // // // override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // if !self.backgroundView.bounds.contains(self.convert(point, to: self.backgroundView)) { // return self.dimView // } // // return super.hitTest(point, with: event) // } // // private func animateOut(completion: @escaping () -> Void) { // self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false) // self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.bounds.height - self.scrollView.contentInset.top), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, removeOnCompletion: false, additive: true, completion: { _ in // completion() // }) // } // // func update(component: ExpandingSheetComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { // component.animateOut.connect { [weak self] completion in // guard let strongSelf = self else { // return // } // strongSelf.animateOut { // completion(Void()) // } // } // // if self.backgroundView.backgroundColor != component.backgroundColor { // self.backgroundView.backgroundColor = component.backgroundColor // } // // transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil) // // let contentSize = self.contentView.update( // transition: transition, // component: component.content, // environment: { // environment[ChildEnvironmentType.self] // }, // containerSize: CGSize(width: availableSize.width, height: .greatestFiniteMagnitude) // ) // // transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil) // transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil) // transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil) // self.scrollView.contentSize = contentSize // self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height), left: 0.0, bottom: 0.0, right: 0.0) // // if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) { // self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) // self.scrollView.layer.animatePosition(from: CGPoint(x: 0.0, y: availableSize.height - self.scrollView.contentInset.top), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: nil) // } else if !environment[SheetComponentEnvironment.self].value.isDisplaying, self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) { // self.animateOut(completion: {}) // } // self.previousIsDisplaying = environment[SheetComponentEnvironment.self].value.isDisplaying // // self.dismiss = environment[SheetComponentEnvironment.self].value.dismiss // // return availableSize // } // } // // public func makeView() -> View { // return View(frame: CGRect()) // } // // public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { // return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) // } //}