import Foundation import UIKit import Display import AsyncDisplayKit import ComponentFlow import SwiftSignalKit import ViewControllerComponent import ComponentDisplayAdapters import TelegramPresentationData import TelegramStringFormatting import AccountContext import TelegramCore import MultilineTextComponent import GiftAnimationComponent private final class GiftContextPreviewComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext let gift: ProfileGiftsContext.State.StarGift init( context: AccountContext, gift: ProfileGiftsContext.State.StarGift ) { self.context = context self.gift = gift } static func ==(lhs: GiftContextPreviewComponent, rhs: GiftContextPreviewComponent) -> Bool { if lhs.context !== rhs.context { return false } if lhs.gift != rhs.gift { return false } return true } public final class View: UIView { private let animation = ComponentView() private let giftCompositionExternalState = GiftCompositionComponent.ExternalState() private let title = ComponentView() private let subtitle = ComponentView() private var component: GiftContextPreviewComponent? private weak var state: EmptyComponentState? private var environment: ViewControllerComponentContainer.Environment? override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .clear } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func update(component: GiftContextPreviewComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let environment = environment[ViewControllerComponentContainer.Environment.self].value self.environment = environment self.component = component self.state = state let subject: GiftCompositionComponent.Subject switch component.gift.gift { case let .generic(gift): subject = .generic(gift.file) case let .unique(gift): subject = .unique(nil, gift) } let animationSize = self.animation.update( transition: .immediate, component: AnyComponent(GiftCompositionComponent( context: component.context, theme: environment.theme, subject: subject, animationOffset: nil, animationScale: nil, displayAnimationStars: false, externalState: self.giftCompositionExternalState, requestUpdate: { [weak state] _ in state?.updated() } )), environment: {}, containerSize: availableSize ) if let view = self.animation.view { if view.superview == nil { self.addSubview(view) } view.frame = CGRect(origin: .zero, size: animationSize) } if case let .unique(uniqueGift) = component.gift.gift { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent(text: .plain( NSAttributedString(string: uniqueGift.title, font: Font.semibold(20.0), textColor: .white) ))), environment: {}, containerSize: availableSize ) if let view = self.title.view { if view.superview == nil { self.addSubview(view) } view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: availableSize.height - titleSize.height - 40.0), size: titleSize) } let vibrantColor: UIColor if let previewPatternColor = giftCompositionExternalState.previewPatternColor { vibrantColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3) } else { vibrantColor = UIColor.white.withAlphaComponent(0.6) } let subtitleSize = self.subtitle.update( transition: .immediate, component: AnyComponent(MultilineTextComponent(text: .plain( NSAttributedString(string: "\(environment.strings.Gift_Unique_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))", font: Font.regular(13.0), textColor: vibrantColor) ))), environment: {}, containerSize: availableSize ) if let view = self.subtitle.view { if view.superview == nil { self.addSubview(view) } view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - subtitleSize.width) / 2.0), y: availableSize.height - subtitleSize.height - 20.0), size: subtitleSize) } } return availableSize } } func makeView() -> View { return View() } public func update(view: View, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } final class GiftContextPreviewController: ViewController { fileprivate final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate { private weak var controller: GiftContextPreviewController? fileprivate let componentHost: ComponentView private var presentationData: PresentationData private var validLayout: ContainerViewLayout? init(controller: GiftContextPreviewController) { self.controller = controller self.presentationData = controller.context.sharedContext.currentPresentationData.with { $0 } self.componentHost = ComponentView() super.init() self.backgroundColor = .clear } override func didLoad() { super.didLoad() self.view.disablesInteractiveModalDismiss = true self.view.disablesInteractiveKeyboardGestureRecognizer = true } func requestLayout(transition: ComponentTransition) { if let layout = self.validLayout { self.containerLayoutUpdated(layout: layout, forceUpdate: true, transition: transition) } } func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, animateOut: Bool = false, transition: ComponentTransition) { guard let controller = self.controller else { return } self.validLayout = layout let environment = ViewControllerComponentContainer.Environment( statusBarHeight: 0.0, navigationHeight: 0.0, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, inputHeight: 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, orientation: nil, isVisible: true, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in return self?.controller } ) let componentSize = self.componentHost.update( transition: transition, component: AnyComponent( GiftContextPreviewComponent( context: controller.context, gift: controller.gift ) ), environment: { environment }, forceUpdate: forceUpdate, containerSize: layout.size ) if let componentView = self.componentHost.view { if componentView.superview == nil { componentView.clipsToBounds = true self.view.addSubview(componentView) } let componentFrame = CGRect(origin: .zero, size: componentSize) transition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) } } } fileprivate var node: Node { return self.displayNode as! Node } fileprivate let context: AccountContext fileprivate let gift: ProfileGiftsContext.State.StarGift init( context: AccountContext, gift: ProfileGiftsContext.State.StarGift ) { self.context = context self.gift = gift super.init(navigationBarPresentationData: nil) self.statusBar.statusBarStyle = .Ignore } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadDisplayNode() { self.displayNode = Node(controller: self) super.displayNodeDidLoad() } override func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? { let minSide = min(layout.size.width, layout.size.height) if case .unique = self.gift.gift { return CGSize(width: minSide, height: floor(minSide * 0.66)) } else { return CGSize(width: minSide, height: 180.0) } } override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) (self.displayNode as! Node).containerLayoutUpdated(layout: layout, transition: ComponentTransition(transition)) } }