import Foundation import UIKit import Display import AsyncDisplayKit import ComponentFlow import SwiftSignalKit import TelegramCore import AccountContext import TelegramPresentationData import GiftItemComponent // WinterGram: grid picker of regular star gifts for adding a non-unique gift to the profile. public final class WinterGramGiftPickerScreen: ViewController { private final class Node: ViewControllerTracingNode { private weak var controller: WinterGramGiftPickerScreen? private let context: AccountContext private var presentationData: PresentationData private let scrollNode: ASScrollNode private var itemViews: [ComponentHostView] = [] private var gifts: [StarGift] = [] private var disposable: Disposable? private var keepUpdatedDisposable: Disposable? private var validLayout: ContainerViewLayout? init(controller: WinterGramGiftPickerScreen, context: AccountContext) { self.controller = controller self.context = context self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.scrollNode = ASScrollNode() super.init() self.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor self.scrollNode.view.alwaysBounceVertical = true self.addSubnode(self.scrollNode) // Ensure the gift catalog is cached so the picker is not empty on first open. self.keepUpdatedDisposable = context.engine.payments.keepStarGiftsUpdated().startStrict() self.disposable = (context.engine.payments.cachedStarGifts() |> deliverOnMainQueue).start(next: { [weak self] gifts in guard let self else { return } self.gifts = (gifts ?? []).filter { gift in if case .generic = gift { return true } return false } if let layout = self.validLayout { self.containerLayoutUpdated(layout: layout, transition: .immediate) } }) } deinit { self.disposable?.dispose() self.keepUpdatedDisposable?.dispose() } func containerLayoutUpdated(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.validLayout = layout let topInset = self.controller?.navigationLayout(layout: layout).navigationFrame.maxY ?? ((layout.statusBarHeight ?? 20.0) + 44.0) transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: .zero, size: layout.size)) let columns = 3 let sideInset = layout.safeInsets.left + 12.0 let spacing: CGFloat = 10.0 let availableWidth = layout.size.width - sideInset * 2.0 let itemWidth = floor((availableWidth - spacing * CGFloat(columns - 1)) / CGFloat(columns)) let itemHeight = itemWidth + 34.0 while self.itemViews.count < self.gifts.count { let view = ComponentHostView() self.scrollNode.view.addSubview(view) self.itemViews.append(view) } while self.itemViews.count > self.gifts.count { self.itemViews.removeLast().removeFromSuperview() } for (index, gift) in self.gifts.enumerated() { guard case let .generic(genericGift) = gift else { continue } let column = index % columns let row = index / columns let x = sideInset + CGFloat(column) * (itemWidth + spacing) let y = topInset + 8.0 + CGFloat(row) * (itemHeight + spacing) let view = self.itemViews[index] let _ = view.update( transition: .immediate, component: AnyComponent(GiftItemComponent( context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, subject: .starGift(gift: genericGift, price: "\(genericGift.price)"), isSoldOut: genericGift.soldOut != nil, action: { [weak self] in self?.controller?.selectGift(gift) } )), environment: {}, containerSize: CGSize(width: itemWidth, height: itemHeight) ) view.frame = CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: itemWidth, height: itemHeight)) } let rows = self.gifts.isEmpty ? 0 : (self.gifts.count + columns - 1) / columns let contentHeight = topInset + 8.0 + CGFloat(rows) * (itemHeight + spacing) + layout.intrinsicInsets.bottom + 16.0 self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: max(layout.size.height, contentHeight)) } } private let context: AccountContext private let completion: (StarGift) -> Void public init(context: AccountContext, completion: @escaping (StarGift) -> Void) { self.context = context self.completion = completion let presentationData = context.sharedContext.currentPresentationData.with { $0 } super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) self.title = presentationData.strings.WinterGram_AddVisualGift self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func cancelPressed() { self.dismiss() } fileprivate func selectGift(_ gift: StarGift) { self.completion(gift) self.dismiss() } override public func loadDisplayNode() { self.displayNode = Node(controller: self, context: self.context) self.displayNodeDidLoad() } override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) (self.displayNode as! Node).containerLayoutUpdated(layout: layout, transition: transition) } }