Ilya Laktyushin cdd987e627 Various fixes
2025-03-07 21:56:17 +04:00

277 lines
11 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import ComponentFlow
import SwiftSignalKit
import ViewControllerComponent
import ComponentDisplayAdapters
import TelegramPresentationData
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(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) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))", 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))
}
}