Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,525 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import PresentationDataUtils
import AccountContext
import ComponentFlow
import ViewControllerComponent
import BundleIconComponent
import MultilineTextComponent
import ButtonComponent
import BlurredBackgroundComponent
import ContextUI
final class AddGiftsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let peerId: EnginePeer.Id
let collectionId: Int32
let remainingCount: Int32
let profileGifts: ProfileGiftsContext
init(
context: AccountContext,
peerId: EnginePeer.Id,
collectionId: Int32,
remainingCount: Int32,
profileGifts: ProfileGiftsContext
) {
self.context = context
self.peerId = peerId
self.collectionId = collectionId
self.remainingCount = remainingCount
self.profileGifts = profileGifts
}
static func ==(lhs: AddGiftsScreenComponent, rhs: AddGiftsScreenComponent) -> Bool {
return true
}
private final class ScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
return true
}
}
final class View: UIView, UIScrollViewDelegate {
private let backgroundView: UIView
private let scrollView: ScrollView
private var giftsListView: GiftsListView?
private let buttonBackground = ComponentView<Empty>()
private let buttonSeparator = SimpleLayer()
private let button = ComponentView<Empty>()
private var isUpdating: Bool = false
private var component: AddGiftsScreenComponent?
private(set) weak var state: EmptyComponentState?
private var environment: EnvironmentType?
override init(frame: CGRect) {
self.backgroundView = UIView()
self.scrollView = ScrollView()
self.scrollView.showsVerticalScrollIndicator = true
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.scrollsToTop = false
self.scrollView.delaysContentTouches = false
self.scrollView.canCancelContentTouches = true
self.scrollView.contentInsetAdjustmentBehavior = .never
if #available(iOS 13.0, *) {
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
}
self.scrollView.alwaysBounceVertical = true
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.scrollView.delegate = self
self.addSubview(self.scrollView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
func scrollToTop() {
self.scrollView.setContentOffset(CGPoint(), animated: true)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateScrolling(interactive: true, transition: .immediate)
}
private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) {
guard let environment = self.environment, let giftsListView = self.giftsListView else {
return
}
let visibleBounds = self.scrollView.bounds.insetBy(dx: 0.0, dy: -10.0)
let contentHeight = giftsListView.updateScrolling(topInset: environment.navigationHeight + 10.0, visibleBounds: visibleBounds, transition: transition)
var contentSize = CGSize(width: self.scrollView.bounds.width, height: contentHeight)
contentSize.height += environment.safeInsets.bottom
contentSize.height = max(contentSize.height, self.scrollView.bounds.size.height)
contentSize.height += 50.0 + 24.0
transition.setFrame(view: giftsListView, frame: CGRect(origin: CGPoint(), size: contentSize))
if self.scrollView.contentSize != contentSize {
self.scrollView.contentSize = contentSize
}
let bottomContentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height)
if interactive, bottomContentOffset < 200.0 {
self.giftsListView?.loadMore()
}
}
func update(component: AddGiftsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
self.isUpdating = true
defer {
self.isUpdating = false
}
let giftsListView: GiftsListView
if let current = self.giftsListView {
giftsListView = current
} else {
giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId, remainingSelectionCount: component.remainingCount)
giftsListView.onContentUpdated = { [weak self] in
guard let self else {
return
}
self.state?.updated(transition: .immediate)
}
giftsListView.selectionUpdated = { [weak self] in
guard let self else {
return
}
self.state?.updated(transition: .spring(duration: 0.4))
}
self.scrollView.addSubview(giftsListView)
self.giftsListView = giftsListView
}
let environment = environment[EnvironmentType.self].value
self.environment = environment
self.component = component
self.state = state
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
let buttonHeight: CGFloat = 50.0
let bottomPanelPadding: CGFloat = 12.0
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
let bottomPanelOffset: CGFloat = giftsListView.selectedItems.count > 0 ? 0.0 : bottomPanelHeight
let buttonString = environment.strings.AddGifts_AddGifts(Int32(giftsListView.selectedItems.count))
let bottomPanelSize = self.buttonBackground.update(
transition: transition,
component: AnyComponent(BlurredBackgroundComponent(
color: environment.theme.rootController.tabBar.backgroundColor
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: bottomPanelHeight)
)
self.buttonSeparator.backgroundColor = environment.theme.rootController.tabBar.separatorColor.cgColor
if let view = self.buttonBackground.view {
if view.superview == nil {
self.addSubview(view)
self.layer.addSublayer(self.buttonSeparator)
}
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height + bottomPanelOffset), size: bottomPanelSize))
transition.setFrame(layer: self.buttonSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height + bottomPanelOffset), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
}
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
let buttonSize = self.button.update(
transition: transition,
component: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
color: environment.theme.list.itemCheckColors.fillColor,
foreground: environment.theme.list.itemCheckColors.foregroundColor,
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
cornerRadius: 10.0
),
content: AnyComponentWithIdentity(
id: AnyHashable(buttonAttributedString.string),
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
),
action: { [weak self] in
guard let self, let controller = self.environment?.controller() as? AddGiftsScreen, let giftsListView = self.giftsListView else {
return
}
controller.completion(giftsListView.selectedItems)
controller.dismiss(animated: true)
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: buttonHeight)
)
if let buttonView = self.button.view {
if buttonView.superview == nil {
self.addSubview(buttonView)
}
transition.setFrame(view: buttonView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - buttonSize.width) / 2.0), y: availableSize.height - bottomPanelHeight + bottomPanelPadding + bottomPanelOffset), size: buttonSize))
}
let visibleBounds = self.scrollView.bounds.insetBy(dx: 0.0, dy: -10.0)
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
let _ = giftsListView.update(size: availableSize, sideInset: 0.0, bottomInset: max(environment.safeInsets.bottom, bottomPanelHeight), deviceMetrics: environment.deviceMetrics, visibleHeight: availableSize.height, isScrollingLockedAtTop: false, expandProgress: 0.0, presentationData: presentationData, synchronous: false, visibleBounds: visibleBounds, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: availableSize))
self.backgroundView.backgroundColor = environment.theme.list.blocksBackgroundColor
transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: availableSize))
self.updateScrolling(transition: transition)
return availableSize
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let r = super.hitTest(point, with: event)
return r
}
}
func makeView() -> View {
return View()
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
public final class AddGiftsScreen: ViewControllerComponentContainer {
private let context: AccountContext
private let peerId: EnginePeer.Id
private let collectionId: Int32
fileprivate let completion: ([ProfileGiftsContext.State.StarGift]) -> Void
private let profileGifts: ProfileGiftsContext
private let filterButton: FilterHeaderButton
public init(
context: AccountContext,
peerId: EnginePeer.Id,
collectionId: Int32,
remainingCount: Int32,
completion: @escaping ([ProfileGiftsContext.State.StarGift]) -> Void
) {
self.context = context
self.peerId = peerId
self.collectionId = collectionId
self.completion = completion
self.profileGifts = ProfileGiftsContext(account: context.account, peerId: peerId)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.filterButton = FilterHeaderButton(presentationData: presentationData)
super.init(context: context, component: AddGiftsScreenComponent(
context: context,
peerId: peerId,
collectionId: collectionId,
remainingCount: remainingCount,
profileGifts: self.profileGifts
), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil)
self.title = presentationData.strings.AddGifts_Title
self.navigationPresentation = .modal
self.scrollToTop = { [weak self] in
guard let self, let componentView = self.node.hostView.componentView as? AddGiftsScreenComponent.View else {
return
}
componentView.scrollToTop()
}
self.filterButton.contextAction = { [weak self] sourceNode, gesture in
self?.presentContextMenu(sourceView: sourceNode.view, gesture: gesture)
}
self.filterButton.addTarget(self, action: #selector(self.filterPressed), forControlEvents: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.filterButton)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func presentContextMenu(sourceView: UIView, gesture: ContextGesture?) {
let giftsContext = self.profileGifts
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let strings = presentationData.strings
let items: Signal<ContextController.Items, NoError> = giftsContext.state
|> map { state in
var hasPinnedGifts = false
for gift in state.gifts {
if gift.pinnedToTop {
hasPinnedGifts = true
break
}
}
return (state.filter, state.sorting, hasPinnedGifts)
}
|> distinctUntilChanged(isEqual: { lhs, rhs -> Bool in
let filterEquals = lhs.0 == rhs.0
let sortingEquals = lhs.1 == rhs.1
let hasPinnedGiftsEquals = lhs.2 == rhs.2
return filterEquals && sortingEquals && hasPinnedGiftsEquals
})
|> map { [weak giftsContext] filter, sorting, hasPinnedGifts -> ContextController.Items in
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor)
}, action: { [weak giftsContext] _, f in
f(.default)
giftsContext?.updateSorting(sorting == .date ? .value : .date)
})))
items.append(.separator)
let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in
var updatedFilter = filter
if updatedFilter.contains(value) {
updatedFilter.remove(value)
} else {
updatedFilter.insert(value)
}
if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limitedUpgradable) && !updatedFilter.contains(.limitedNonUpgradable) && !updatedFilter.contains(.unique) {
updatedFilter.insert(.unlimited)
}
if !updatedFilter.contains(.displayed) && !updatedFilter.contains(.hidden) {
if value == .displayed {
updatedFilter.insert(.hidden)
} else {
updatedFilter.insert(.displayed)
}
}
giftsContext?.updateFilter(updatedFilter)
}
let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in
var updatedFilter = filter
updatedFilter.remove(.unlimited)
updatedFilter.remove(.limitedUpgradable)
updatedFilter.remove(.limitedNonUpgradable)
updatedFilter.remove(.unique)
updatedFilter.insert(value)
giftsContext?.updateFilter(updatedFilter)
}
let switchToVisiblityFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in
var updatedFilter = filter
updatedFilter.remove(.hidden)
updatedFilter.remove(.displayed)
updatedFilter.insert(value)
giftsContext?.updateFilter(updatedFilter)
}
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.unlimited)
}, longPressAction: { _, f in
switchToFilter(.unlimited)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in
return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.limitedNonUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedNonUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in
return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.limitedUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.unique)
}, longPressAction: { _, f in
switchToFilter(.unique)
})))
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.displayed)
}, longPressAction: { _, f in
switchToVisiblityFilter(.displayed)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
toggleFilter(.hidden)
}, longPressAction: { _, f in
switchToVisiblityFilter(.hidden)
})))
return ContextController.Items(content: .list(items))
}
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items, gesture: gesture)
self.presentInGlobalOverlay(contextController)
}
deinit {
}
@objc private func cancelPressed() {
self.dismiss()
}
@objc private func filterPressed() {
self.filterButton.contextAction?(self.filterButton.containerNode, nil)
}
}
private final class FilterHeaderButton: HighlightableButtonNode {
let referenceNode: ContextReferenceContentNode
let containerNode: ContextControllerSourceNode
private let icon = ComponentView<Empty>()
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
init(presentationData: PresentationData) {
self.referenceNode = ContextReferenceContentNode()
self.containerNode = ContextControllerSourceNode()
self.containerNode.animateScale = false
super.init()
self.containerNode.addSubnode(self.referenceNode)
self.addSubnode(self.containerNode)
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self, let _ = strongSelf.contextAction else {
return false
}
return true
}
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}
strongSelf.contextAction?(strongSelf.containerNode, gesture)
}
self.update(theme: presentationData.theme, strings: presentationData.strings)
}
func update(theme: PresentationTheme, strings: PresentationStrings) {
let iconSize = self.icon.update(
transition: .immediate,
component: AnyComponent(
BundleIconComponent(
name: "Peer Info/SortIcon",
tintColor: theme.rootController.navigationBar.accentTextColor
)
),
environment: {},
containerSize: CGSize(width: 30.0, height: 30.0)
)
if let view = self.icon.view {
if view.superview == nil {
view.isUserInteractionEnabled = false
self.referenceNode.view.addSubview(view)
}
view.frame = CGRect(origin: CGPoint(x: 14.0, y: 7.0), size: iconSize)
}
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 44.0, height: 44.0))
self.referenceNode.frame = self.containerNode.bounds
}
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
return CGSize(width: 44.0, height: 44.0)
}
func onLayout() {
}
}
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceView: UIView
init(controller: ViewController, sourceView: UIView) {
self.controller = controller
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
@@ -0,0 +1,277 @@
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<Empty>()
private let giftCompositionExternalState = GiftCompositionComponent.ExternalState()
private let title = ComponentView<Empty>()
private let subtitle = ComponentView<Empty>()
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<ViewControllerComponentContainer.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<ViewControllerComponentContainer.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<ViewControllerComponentContainer.Environment>
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<ViewControllerComponentContainer.Environment>()
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))
}
}