mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-18 17:21:08 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
18e66ecea2
@ -23,6 +23,7 @@ swift_library(
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -3,41 +3,12 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import PassKit
|
||||
import ShimmerEffect
|
||||
|
||||
enum BotCheckoutActionButtonState: Equatable {
|
||||
case loading
|
||||
case active(String)
|
||||
case inactive(String)
|
||||
case applePay
|
||||
|
||||
static func ==(lhs: BotCheckoutActionButtonState, rhs: BotCheckoutActionButtonState) -> Bool {
|
||||
switch lhs {
|
||||
case .loading:
|
||||
if case .loading = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .active(title):
|
||||
if case .active(title) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .inactive(title):
|
||||
if case .inactive(title) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .applePay:
|
||||
if case .applePay = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
case placeholder
|
||||
}
|
||||
|
||||
private let titleFont = Font.semibold(17.0)
|
||||
@ -45,49 +16,24 @@ private let titleFont = Font.semibold(17.0)
|
||||
final class BotCheckoutActionButton: HighlightableButtonNode {
|
||||
static var height: CGFloat = 52.0
|
||||
|
||||
private var inactiveFillColor: UIColor
|
||||
private var activeFillColor: UIColor
|
||||
private var foregroundColor: UIColor
|
||||
|
||||
private let progressBackgroundNode: ASImageNode
|
||||
private let inactiveBackgroundNode: ASImageNode
|
||||
private let activeBackgroundNode: ASImageNode
|
||||
private var applePayButton: UIButton?
|
||||
private let labelNode: TextNode
|
||||
|
||||
private var state: BotCheckoutActionButtonState?
|
||||
private var validLayout: CGSize?
|
||||
private var validLayout: (CGRect, CGSize)?
|
||||
|
||||
init(inactiveFillColor: UIColor, activeFillColor: UIColor, foregroundColor: UIColor) {
|
||||
self.inactiveFillColor = inactiveFillColor
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
|
||||
init(activeFillColor: UIColor, foregroundColor: UIColor) {
|
||||
self.activeFillColor = activeFillColor
|
||||
self.foregroundColor = foregroundColor
|
||||
|
||||
let diameter: CGFloat = 20.0
|
||||
|
||||
self.progressBackgroundNode = ASImageNode()
|
||||
self.progressBackgroundNode.displaysAsynchronously = false
|
||||
self.progressBackgroundNode.displayWithoutProcessing = true
|
||||
self.progressBackgroundNode.isLayerBacked = true
|
||||
self.progressBackgroundNode.image = generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
let strokeWidth: CGFloat = 2.0
|
||||
context.setFillColor(activeFillColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(inactiveFillColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: strokeWidth, y: strokeWidth), size: CGSize(width: size.width - strokeWidth * 2.0, height: size.height - strokeWidth * 2.0)))
|
||||
let cutout: CGFloat = diameter
|
||||
context.fill(CGRect(origin: CGPoint(x: floor((size.width - cutout) / 2.0), y: 0.0), size: CGSize(width: cutout, height: cutout)))
|
||||
})
|
||||
|
||||
self.inactiveBackgroundNode = ASImageNode()
|
||||
self.inactiveBackgroundNode.displaysAsynchronously = false
|
||||
self.inactiveBackgroundNode.displayWithoutProcessing = true
|
||||
self.inactiveBackgroundNode.isLayerBacked = true
|
||||
self.inactiveBackgroundNode.image = generateStretchableFilledCircleImage(diameter: diameter, color: self.foregroundColor, strokeColor: activeFillColor, strokeWidth: 2.0)
|
||||
self.inactiveBackgroundNode.alpha = 0.0
|
||||
|
||||
self.activeBackgroundNode = ASImageNode()
|
||||
self.activeBackgroundNode.displaysAsynchronously = false
|
||||
self.activeBackgroundNode.displayWithoutProcessing = true
|
||||
@ -100,8 +46,6 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.progressBackgroundNode)
|
||||
self.addSubnode(self.inactiveBackgroundNode)
|
||||
self.addSubnode(self.activeBackgroundNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
}
|
||||
@ -111,136 +55,8 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
||||
let previousState = self.state
|
||||
self.state = state
|
||||
|
||||
if let validLayout = self.validLayout, let previousState = previousState {
|
||||
switch state {
|
||||
case .loading:
|
||||
self.inactiveBackgroundNode.layer.animateFrame(from: self.inactiveBackgroundNode.frame, to: self.progressBackgroundNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
if !self.inactiveBackgroundNode.alpha.isZero {
|
||||
self.inactiveBackgroundNode.alpha = 0.0
|
||||
self.inactiveBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
}
|
||||
self.activeBackgroundNode.layer.animateFrame(from: self.activeBackgroundNode.frame, to: self.progressBackgroundNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.activeBackgroundNode.alpha = 0.0
|
||||
self.activeBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
self.labelNode.alpha = 0.0
|
||||
self.labelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
|
||||
self.progressBackgroundNode.alpha = 1.0
|
||||
self.progressBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
|
||||
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
basicAnimation.duration = 0.8
|
||||
basicAnimation.fromValue = NSNumber(value: Float(0.0))
|
||||
basicAnimation.toValue = NSNumber(value: Float.pi * 2.0)
|
||||
basicAnimation.repeatCount = Float.infinity
|
||||
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
|
||||
self.progressBackgroundNode.layer.add(basicAnimation, forKey: "progressRotation")
|
||||
case let .active(title):
|
||||
if let applePayButton = self.applePayButton {
|
||||
self.applePayButton = nil
|
||||
applePayButton.removeFromSuperview()
|
||||
}
|
||||
|
||||
if case .active = previousState {
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: validLayout, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.width - labelLayout.size.width) / 2.0), y: floor((validLayout.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
let _ = labelApply()
|
||||
} else {
|
||||
self.inactiveBackgroundNode.layer.animateFrame(from: self.progressBackgroundNode.frame, to: self.activeBackgroundNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.inactiveBackgroundNode.alpha = 1.0
|
||||
self.progressBackgroundNode.alpha = 0.0
|
||||
|
||||
self.activeBackgroundNode.layer.animateFrame(from: self.progressBackgroundNode.frame, to: self.activeBackgroundNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.activeBackgroundNode.alpha = 1.0
|
||||
self.activeBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: validLayout, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.width - labelLayout.size.width) / 2.0), y: floor((validLayout.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
let _ = labelApply()
|
||||
self.labelNode.alpha = 1.0
|
||||
self.labelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
case let .inactive(title):
|
||||
if case .inactive = previousState {
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.activeFillColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: validLayout, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.width - labelLayout.size.width) / 2.0), y: floor((validLayout.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
let _ = labelApply()
|
||||
} else {
|
||||
self.inactiveBackgroundNode.layer.animateFrame(from: self.inactiveBackgroundNode.frame, to: self.activeBackgroundNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.inactiveBackgroundNode.alpha = 1.0
|
||||
self.progressBackgroundNode.alpha = 0.0
|
||||
|
||||
self.activeBackgroundNode.alpha = 0.0
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: validLayout, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.width - labelLayout.size.width) / 2.0), y: floor((validLayout.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
let _ = labelApply()
|
||||
self.labelNode.alpha = 1.0
|
||||
self.labelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
case .applePay:
|
||||
if self.applePayButton == nil {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
let applePayButton: PKPaymentButton
|
||||
if #available(iOS 14.0, *) {
|
||||
applePayButton = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
|
||||
} else {
|
||||
applePayButton = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
|
||||
}
|
||||
applePayButton.addTarget(self, action: #selector(self.applePayButtonPressed), for: .touchUpInside)
|
||||
self.view.addSubview(applePayButton)
|
||||
self.applePayButton = applePayButton
|
||||
}
|
||||
}
|
||||
if let applePayButton = self.applePayButton {
|
||||
applePayButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: validLayout.width, height: BotCheckoutActionButton.height))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch state {
|
||||
case .loading:
|
||||
self.labelNode.alpha = 0.0
|
||||
self.progressBackgroundNode.alpha = 1.0
|
||||
self.activeBackgroundNode.alpha = 0.0
|
||||
|
||||
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
basicAnimation.duration = 0.8
|
||||
basicAnimation.fromValue = NSNumber(value: Float(0.0))
|
||||
basicAnimation.toValue = NSNumber(value: Float.pi * 2.0)
|
||||
basicAnimation.repeatCount = Float.infinity
|
||||
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
|
||||
self.progressBackgroundNode.layer.add(basicAnimation, forKey: "progressRotation")
|
||||
case .active:
|
||||
self.labelNode.alpha = 1.0
|
||||
self.progressBackgroundNode.alpha = 0.0
|
||||
self.inactiveBackgroundNode.alpha = 0.0
|
||||
self.activeBackgroundNode.alpha = 1.0
|
||||
case .inactive:
|
||||
self.labelNode.alpha = 1.0
|
||||
self.progressBackgroundNode.alpha = 0.0
|
||||
self.inactiveBackgroundNode.alpha = 1.0
|
||||
self.activeBackgroundNode.alpha = 0.0
|
||||
case .applePay:
|
||||
self.labelNode.alpha = 0.0
|
||||
self.progressBackgroundNode.alpha = 0.0
|
||||
self.inactiveBackgroundNode.alpha = 0.0
|
||||
self.activeBackgroundNode.alpha = 0.0
|
||||
if self.applePayButton == nil {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
let applePayButton = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
|
||||
self.view.addSubview(applePayButton)
|
||||
self.applePayButton = applePayButton
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (absoluteRect, containerSize) = self.validLayout, let previousState = previousState {
|
||||
self.updateLayout(absoluteRect: absoluteRect, containerSize: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,33 +65,81 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
||||
self.sendActions(forControlEvents: .touchUpInside, with: nil)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
func updateLayout(absoluteRect: CGRect, containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let size = absoluteRect.size
|
||||
|
||||
self.validLayout = (absoluteRect, containerSize)
|
||||
|
||||
transition.updateFrame(node: self.progressBackgroundNode, frame: CGRect(origin: CGPoint(x: floor((size.width - BotCheckoutActionButton.height) / 2.0), y: 0.0), size: CGSize(width: BotCheckoutActionButton.height, height: BotCheckoutActionButton.height)))
|
||||
transition.updateFrame(node: self.inactiveBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: BotCheckoutActionButton.height)))
|
||||
transition.updateFrame(node: self.activeBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: BotCheckoutActionButton.height)))
|
||||
if let applePayButton = self.applePayButton {
|
||||
applePayButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: BotCheckoutActionButton.height))
|
||||
}
|
||||
|
||||
var labelSize = self.labelNode.bounds.size
|
||||
if let state = self.state {
|
||||
switch state {
|
||||
case let .active(title):
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: size, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let _ = labelApply()
|
||||
labelSize = labelLayout.size
|
||||
case let .inactive(title):
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.activeFillColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: size, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let _ = labelApply()
|
||||
labelSize = labelLayout.size
|
||||
default:
|
||||
break
|
||||
case let .active(title):
|
||||
if let applePayButton = self.applePayButton {
|
||||
self.applePayButton = nil
|
||||
applePayButton.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let placeholderNode = self.placeholderNode {
|
||||
self.placeholderNode = nil
|
||||
placeholderNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: size, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let _ = labelApply()
|
||||
labelSize = labelLayout.size
|
||||
case .applePay:
|
||||
if self.applePayButton == nil {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
let applePayButton: PKPaymentButton
|
||||
if #available(iOS 14.0, *) {
|
||||
applePayButton = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
|
||||
} else {
|
||||
applePayButton = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
|
||||
}
|
||||
applePayButton.addTarget(self, action: #selector(self.applePayButtonPressed), for: .touchUpInside)
|
||||
self.view.addSubview(applePayButton)
|
||||
self.applePayButton = applePayButton
|
||||
}
|
||||
}
|
||||
|
||||
if let placeholderNode = self.placeholderNode {
|
||||
self.placeholderNode = nil
|
||||
placeholderNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let applePayButton = self.applePayButton {
|
||||
applePayButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: BotCheckoutActionButton.height))
|
||||
}
|
||||
case .placeholder:
|
||||
if let applePayButton = self.applePayButton {
|
||||
self.applePayButton = nil
|
||||
applePayButton.removeFromSuperview()
|
||||
}
|
||||
|
||||
let contentSize = CGSize(width: 80.0, height: 8.0)
|
||||
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = self.placeholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
self.placeholderNode = shimmerNode
|
||||
self.addSubnode(shimmerNode)
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(x: floor((size.width - contentSize.width) / 2.0), y: floor((size.height - contentSize.height) / 2.0)), size: contentSize)
|
||||
shimmerNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: absoluteRect.minX + shimmerNode.frame.minX, y: absoluteRect.minY + shimmerNode.frame.minY), size: contentSize), within: containerSize)
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: 0.0, y: 0.0), width: contentSize.width, diameter: contentSize.height))
|
||||
|
||||
shimmerNode.update(backgroundColor: self.activeFillColor, foregroundColor: self.activeFillColor.mixedWith(UIColor.white, alpha: 0.25), shimmeringColor: self.activeFillColor.mixedWith(UIColor.white, alpha: 0.15), shapes: shapes, size: contentSize)
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) / 2.0), y: floor((size.height - labelSize.height) / 2.0)), size: labelSize))
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,64 @@ import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
public final class BotCheckoutController: ViewController {
|
||||
public final class InputData {
|
||||
public enum FetchError {
|
||||
case generic
|
||||
}
|
||||
|
||||
let form: BotPaymentForm
|
||||
let validatedFormInfo: BotPaymentValidatedFormInfo?
|
||||
|
||||
private init(
|
||||
form: BotPaymentForm,
|
||||
validatedFormInfo: BotPaymentValidatedFormInfo?
|
||||
) {
|
||||
self.form = form
|
||||
self.validatedFormInfo = validatedFormInfo
|
||||
}
|
||||
|
||||
public static func fetch(context: AccountContext, messageId: MessageId) -> Signal<InputData, FetchError> {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let themeParams: [String: Any] = [
|
||||
"bg_color": Int32(bitPattern: presentationData.theme.list.plainBackgroundColor.argb),
|
||||
"text_color": Int32(bitPattern: presentationData.theme.list.itemPrimaryTextColor.argb),
|
||||
"link_color": Int32(bitPattern: presentationData.theme.list.itemAccentColor.argb),
|
||||
"button_color": Int32(bitPattern: presentationData.theme.list.itemCheckColors.fillColor.argb),
|
||||
"button_text_color": Int32(bitPattern: presentationData.theme.list.itemCheckColors.foregroundColor.argb)
|
||||
]
|
||||
|
||||
return fetchBotPaymentForm(postbox: context.account.postbox, network: context.account.network, messageId: messageId, themeParams: themeParams)
|
||||
|> mapError { _ -> FetchError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { paymentForm -> Signal<InputData, FetchError> in
|
||||
if let current = paymentForm.savedInfo {
|
||||
return validateBotPaymentForm(account: context.account, saveInfo: true, messageId: messageId, formInfo: current)
|
||||
|> mapError { _ -> FetchError in
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> InputData in
|
||||
return InputData(
|
||||
form: paymentForm,
|
||||
validatedFormInfo: result
|
||||
)
|
||||
}
|
||||
|> `catch` { _ -> Signal<InputData, FetchError> in
|
||||
return .single(InputData(
|
||||
form: paymentForm,
|
||||
validatedFormInfo: nil
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return .single(InputData(
|
||||
form: paymentForm,
|
||||
validatedFormInfo: nil
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var controllerNode: BotCheckoutControllerNode {
|
||||
return self.displayNode as! BotCheckoutControllerNode
|
||||
}
|
||||
@ -27,10 +85,13 @@ public final class BotCheckoutController: ViewController {
|
||||
|
||||
private var didPlayPresentationAnimation = false
|
||||
|
||||
public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId) {
|
||||
private let inputData: Promise<BotCheckoutController.InputData?>
|
||||
|
||||
public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>) {
|
||||
self.context = context
|
||||
self.invoice = invoice
|
||||
self.messageId = messageId
|
||||
self.inputData = inputData
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -56,7 +117,7 @@ public final class BotCheckoutController: ViewController {
|
||||
if let strongSelf = self {
|
||||
strongSelf.navigationOffset = offset
|
||||
}
|
||||
}, context: self.context, invoice: self.invoice, messageId: self.messageId, present: { [weak self] c, a in
|
||||
}, context: self.context, invoice: self.invoice, messageId: self.messageId, inputData: self.inputData, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, dismissAnimated: { [weak self] in
|
||||
self?.dismiss()
|
||||
|
@ -45,8 +45,21 @@ private enum BotCheckoutSection: Int32 {
|
||||
}
|
||||
|
||||
enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
enum StableId: Hashable {
|
||||
case header
|
||||
case price(Int)
|
||||
case actionPlaceholder(Int)
|
||||
case tip
|
||||
case paymentMethod
|
||||
case shippingInfo
|
||||
case shippingMethod
|
||||
case nameInfo
|
||||
case emailInfo
|
||||
case phoneInfo
|
||||
}
|
||||
|
||||
case header(PresentationTheme, TelegramMediaInvoice, String)
|
||||
case price(Int, PresentationTheme, String, String, Bool, Bool)
|
||||
case price(Int, PresentationTheme, String, String, Bool, Bool, Int?)
|
||||
case tip(Int, PresentationTheme, String, String, String, Int64, Int64, [(String, Int64)])
|
||||
case paymentMethod(PresentationTheme, String, String)
|
||||
case shippingInfo(PresentationTheme, String, String)
|
||||
@ -54,6 +67,7 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
case nameInfo(PresentationTheme, String, String)
|
||||
case emailInfo(PresentationTheme, String, String)
|
||||
case phoneInfo(PresentationTheme, String, String)
|
||||
case actionPlaceholder(Int, Int)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -66,14 +80,16 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
var sortId: Int32 {
|
||||
switch self {
|
||||
case .header:
|
||||
return 0
|
||||
case let .price(index, _, _, _, _, _):
|
||||
case let .price(index, _, _, _, _, _, _):
|
||||
return 1 + Int32(index)
|
||||
case let .tip(index, _, _, _, _, _, _, _):
|
||||
return 1 + Int32(index)
|
||||
case let .actionPlaceholder(index, _):
|
||||
return 1 + Int32(index)
|
||||
case .paymentMethod:
|
||||
return 10000 + 2
|
||||
case .shippingInfo:
|
||||
@ -89,6 +105,31 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: StableId {
|
||||
switch self {
|
||||
case .header:
|
||||
return .header
|
||||
case let .price(index, _, _, _, _, _, _):
|
||||
return .price(index)
|
||||
case .tip:
|
||||
return .tip
|
||||
case let .actionPlaceholder(index, _):
|
||||
return .actionPlaceholder(index)
|
||||
case .paymentMethod:
|
||||
return .paymentMethod
|
||||
case .shippingInfo:
|
||||
return .shippingInfo
|
||||
case .shippingMethod:
|
||||
return .shippingMethod
|
||||
case .nameInfo:
|
||||
return .nameInfo
|
||||
case .emailInfo:
|
||||
return .emailInfo
|
||||
case .phoneInfo:
|
||||
return .phoneInfo
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: BotCheckoutEntry, rhs: BotCheckoutEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .header(lhsTheme, lhsInvoice, lhsName):
|
||||
@ -106,8 +147,8 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .price(lhsIndex, lhsTheme, lhsText, lhsValue, lhsFinal, lhsHasSeparator):
|
||||
if case let .price(rhsIndex, rhsTheme, rhsText, rhsValue, rhsFinal, rhsHasSeparator) = rhs {
|
||||
case let .price(lhsIndex, lhsTheme, lhsText, lhsValue, lhsFinal, lhsHasSeparator, lhsShimmeringIndex):
|
||||
if case let .price(rhsIndex, rhsTheme, rhsText, rhsValue, rhsFinal, rhsHasSeparator, rhsShimmeringIndex) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -126,6 +167,9 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
if lhsHasSeparator != rhsHasSeparator {
|
||||
return false
|
||||
}
|
||||
if lhsShimmeringIndex != rhsShimmeringIndex {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -183,11 +227,17 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .actionPlaceholder(index, shimmeringIndex):
|
||||
if case .actionPlaceholder(index, shimmeringIndex) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: BotCheckoutEntry, rhs: BotCheckoutEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
return lhs.sortId < rhs.sortId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
@ -195,8 +245,8 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case let .header(theme, invoice, botName):
|
||||
return BotCheckoutHeaderItem(account: arguments.account, theme: theme, invoice: invoice, botName: botName, sectionId: self.section)
|
||||
case let .price(_, theme, text, value, isFinal, hasSeparator):
|
||||
return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, hasSeparator: hasSeparator, sectionId: self.section)
|
||||
case let .price(_, theme, text, value, isFinal, hasSeparator, shimmeringIndex):
|
||||
return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, hasSeparator: hasSeparator, shimmeringIndex: shimmeringIndex, sectionId: self.section)
|
||||
case let .tip(_, _, text, currency, value, numericValue, maxValue, variants):
|
||||
return BotCheckoutTipItem(theme: presentationData.theme, strings: presentationData.strings, title: text, currency: currency, value: value, numericValue: numericValue, maxValue: maxValue, availableVariants: variants, sectionId: self.section, updateValue: { value in
|
||||
arguments.updateTip(value)
|
||||
@ -229,6 +279,9 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openInfo(.phone)
|
||||
})
|
||||
case let .actionPlaceholder(_, shimmeringIndex):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: " ", label: " ", sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
}, shimmeringIndex: shimmeringIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,7 +346,7 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
|
||||
|
||||
var index = 0
|
||||
for price in paymentForm.invoice.prices {
|
||||
entries.append(.price(index, presentationData.theme, price.label, formatCurrencyAmount(price.amount, currency: paymentForm.invoice.currency), false, index == 0))
|
||||
entries.append(.price(index, presentationData.theme, price.label, formatCurrencyAmount(price.amount, currency: paymentForm.invoice.currency), false, index == 0, nil))
|
||||
totalPrice += price.amount
|
||||
index += 1
|
||||
}
|
||||
@ -307,7 +360,7 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
|
||||
shippingOptionString = option.title
|
||||
|
||||
for price in option.prices {
|
||||
entries.append(.price(index, presentationData.theme, price.label, formatCurrencyAmount(price.amount, currency: paymentForm.invoice.currency), false, false))
|
||||
entries.append(.price(index, presentationData.theme, price.label, formatCurrencyAmount(price.amount, currency: paymentForm.invoice.currency), false, false, nil))
|
||||
totalPrice += price.amount
|
||||
index += 1
|
||||
}
|
||||
@ -320,8 +373,8 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
|
||||
|
||||
if !entries.isEmpty {
|
||||
switch entries[entries.count - 1] {
|
||||
case let .price(index, theme, title, value, _, _):
|
||||
entries[entries.count - 1] = .price(index, theme, title, value, false, false)
|
||||
case let .price(index, theme, title, value, _, _, _):
|
||||
entries[entries.count - 1] = .price(index, theme, title, value, false, index == 0, nil)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -336,7 +389,7 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
|
||||
index += 1
|
||||
}
|
||||
|
||||
entries.append(.price(index, presentationData.theme, presentationData.strings.Checkout_TotalAmount, formatCurrencyAmount(totalPrice, currency: paymentForm.invoice.currency), true, true))
|
||||
entries.append(.price(index, presentationData.theme, presentationData.strings.Checkout_TotalAmount, formatCurrencyAmount(totalPrice, currency: paymentForm.invoice.currency), true, true, nil))
|
||||
|
||||
var paymentMethodTitle = ""
|
||||
if let currentPaymentMethod = currentPaymentMethod {
|
||||
@ -379,6 +432,15 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
|
||||
if paymentForm.invoice.requestedFields.contains(.phone) {
|
||||
entries.append(.phoneInfo(presentationData.theme, presentationData.strings.Checkout_Phone, formInfo?.phone ?? ""))
|
||||
}
|
||||
} else {
|
||||
let numItems = 4
|
||||
for index in 0 ..< numItems {
|
||||
entries.append(.price(index, presentationData.theme, " ", " ", false, index == 0, index))
|
||||
}
|
||||
|
||||
for index in numItems ..< numItems + 2 {
|
||||
entries.append(.actionPlaceholder(index, index - numItems))
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
@ -474,7 +536,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
private var passwordTip: String?
|
||||
private var passwordTipDisposable: Disposable?
|
||||
|
||||
init(controller: BotCheckoutController?, navigationBar: NavigationBar, updateNavigationOffset: @escaping (CGFloat) -> Void, context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void) {
|
||||
init(controller: BotCheckoutController?, navigationBar: NavigationBar, updateNavigationOffset: @escaping (CGFloat) -> Void, context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.messageId = messageId
|
||||
@ -514,9 +576,8 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
self.actionButtonPanelSeparator = ASDisplayNode()
|
||||
self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
self.actionButton = BotCheckoutActionButton(inactiveFillColor: self.presentationData.theme.list.plainBackgroundColor, activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
self.actionButton.setState(.active(""))
|
||||
self.actionButtonPanelNode.isHidden = true
|
||||
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
self.actionButton.setState(.placeholder)
|
||||
|
||||
self.inProgressDimNode = ASDisplayNode()
|
||||
self.inProgressDimNode.alpha = 0.0
|
||||
@ -828,56 +889,33 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}
|
||||
let themeParams: [String: Any] = [
|
||||
"bg_color": Int32(bitPattern: self.presentationData.theme.list.plainBackgroundColor.argb),
|
||||
"text_color": Int32(bitPattern: self.presentationData.theme.list.itemPrimaryTextColor.argb),
|
||||
"link_color": Int32(bitPattern: self.presentationData.theme.list.itemAccentColor.argb),
|
||||
"button_color": Int32(bitPattern: self.presentationData.theme.list.itemCheckColors.fillColor.argb),
|
||||
"button_text_color": Int32(bitPattern: self.presentationData.theme.list.itemCheckColors.foregroundColor.argb)
|
||||
]
|
||||
|
||||
let formAndMaybeValidatedInfo = fetchBotPaymentForm(postbox: context.account.postbox, network: context.account.network, messageId: messageId, themeParams: themeParams)
|
||||
|> mapToSignal { paymentForm -> Signal<(BotPaymentForm, BotPaymentValidatedFormInfo?), BotPaymentFormRequestError> in
|
||||
if let current = paymentForm.savedInfo {
|
||||
return validateBotPaymentForm(account: context.account, saveInfo: true, messageId: messageId, formInfo: current)
|
||||
|> mapError { _ -> BotPaymentFormRequestError in
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> (BotPaymentForm, BotPaymentValidatedFormInfo?) in
|
||||
return (paymentForm, result)
|
||||
}
|
||||
|> `catch` { _ -> Signal<(BotPaymentForm, BotPaymentValidatedFormInfo?), BotPaymentFormRequestError> in
|
||||
return .single((paymentForm, nil))
|
||||
}
|
||||
} else {
|
||||
return .single((paymentForm, nil))
|
||||
}
|
||||
}
|
||||
|
||||
self.formRequestDisposable = (formAndMaybeValidatedInfo |> deliverOnMainQueue).start(next: { [weak self] form, validatedInfo in
|
||||
self.formRequestDisposable = (inputData.get() |> deliverOnMainQueue).start(next: { [weak self] formAndValidatedInfo in
|
||||
if let strongSelf = self {
|
||||
guard let formAndValidatedInfo = formAndValidatedInfo else {
|
||||
strongSelf.controller?.dismiss()
|
||||
return
|
||||
}
|
||||
UIView.transition(with: strongSelf.view, duration: 0.25, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
|
||||
}, completion: nil)
|
||||
|
||||
let savedInfo: BotPaymentRequestedInfo
|
||||
if let current = form.savedInfo {
|
||||
if let current = formAndValidatedInfo.form.savedInfo {
|
||||
savedInfo = current
|
||||
} else {
|
||||
savedInfo = BotPaymentRequestedInfo(name: nil, phone: nil, email: nil, shippingAddress: nil)
|
||||
}
|
||||
strongSelf.paymentFormValue = form
|
||||
strongSelf.paymentFormValue = formAndValidatedInfo.form
|
||||
strongSelf.currentFormInfo = savedInfo
|
||||
strongSelf.currentValidatedFormInfo = validatedInfo
|
||||
if let savedCredentials = form.savedCredentials {
|
||||
strongSelf.currentValidatedFormInfo = formAndValidatedInfo.validatedFormInfo
|
||||
if let savedCredentials = formAndValidatedInfo.form.savedCredentials {
|
||||
strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials)
|
||||
}
|
||||
strongSelf.actionButton.isEnabled = true
|
||||
strongSelf.paymentFormAndInfo.set(.single((form, savedInfo, validatedInfo, nil, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
|
||||
strongSelf.paymentFormAndInfo.set(.single((formAndValidatedInfo.form, savedInfo, formAndValidatedInfo.validatedFormInfo, nil, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
|
||||
|
||||
strongSelf.updateActionButton()
|
||||
}
|
||||
}, error: { _ in
|
||||
|
||||
})
|
||||
|
||||
self.addSubnode(self.actionButtonPanelNode)
|
||||
@ -958,7 +996,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: bottomPanelHorizontalInset, y: bottomPanelVerticalInset), size: CGSize(width: layout.size.width - bottomPanelHorizontalInset * 2.0, height: BotCheckoutActionButton.height))
|
||||
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)
|
||||
self.actionButton.updateLayout(size: actionButtonFrame.size, transition: transition)
|
||||
self.actionButton.updateLayout(absoluteRect: actionButtonFrame.offsetBy(dx: self.actionButtonPanelNode.frame.minX, dy: self.actionButtonPanelNode.frame.minY), containerSize: layout.size, transition: transition)
|
||||
|
||||
updatedInsets.bottom = bottomPanelHeight
|
||||
|
||||
|
@ -6,6 +6,7 @@ import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import ShimmerEffect
|
||||
|
||||
class BotCheckoutPriceItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
@ -13,16 +14,18 @@ class BotCheckoutPriceItem: ListViewItem, ItemListItem {
|
||||
let label: String
|
||||
let isFinal: Bool
|
||||
let hasSeparator: Bool
|
||||
let shimmeringIndex: Int?
|
||||
let sectionId: ItemListSectionId
|
||||
|
||||
let requestsNoInset: Bool = true
|
||||
|
||||
init(theme: PresentationTheme, title: String, label: String, isFinal: Bool, hasSeparator: Bool, sectionId: ItemListSectionId) {
|
||||
init(theme: PresentationTheme, title: String, label: String, isFinal: Bool, hasSeparator: Bool, shimmeringIndex: Int?, sectionId: ItemListSectionId) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.label = label
|
||||
self.isFinal = isFinal
|
||||
self.hasSeparator = hasSeparator
|
||||
self.shimmeringIndex = shimmeringIndex
|
||||
self.sectionId = sectionId
|
||||
}
|
||||
|
||||
@ -90,6 +93,9 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
let separatorNode: ASDisplayNode
|
||||
let bottomSeparatorNode: ASDisplayNode
|
||||
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
private var item: BotCheckoutPriceItem?
|
||||
|
||||
init() {
|
||||
@ -112,6 +118,15 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.placeholderNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: BotCheckoutPriceItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors, _ previousItem: ListViewItem?, _ nextItem: ListViewItem?) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
@ -124,7 +139,12 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
if item.isFinal {
|
||||
naturalContentHeight = 44.0
|
||||
} else {
|
||||
naturalContentHeight = 34.0
|
||||
switch neighbors.bottom {
|
||||
case .otherSection, .none:
|
||||
naturalContentHeight = 44.0
|
||||
default:
|
||||
naturalContentHeight = 34.0
|
||||
}
|
||||
}
|
||||
if let _ = previousItem as? BotCheckoutHeaderItem {
|
||||
verticalOffset += 8.0
|
||||
@ -164,7 +184,13 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
strongSelf.separatorNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: params.width - leftInset, height: UIScreenPixel))
|
||||
|
||||
strongSelf.bottomSeparatorNode.isHidden = !item.isFinal
|
||||
switch neighbors.bottom {
|
||||
case .otherSection, .none:
|
||||
strongSelf.bottomSeparatorNode.isHidden = false
|
||||
default:
|
||||
strongSelf.bottomSeparatorNode.isHidden = !item.isFinal
|
||||
}
|
||||
|
||||
strongSelf.bottomSeparatorNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: contentSize.height), size: CGSize(width: params.width, height: UIScreenPixel))
|
||||
|
||||
@ -173,6 +199,38 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalOffset + floor((naturalContentHeight - titleLayout.size.height) / 2.0)), size: titleLayout.size)
|
||||
strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: params.width - rightInset - labelLayout.size.width, y: verticalOffset + floor((naturalContentHeight - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
|
||||
if let shimmeringIndex = item.shimmeringIndex {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.placeholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.placeholderNode = shimmerNode
|
||||
if strongSelf.separatorNode.supernode != nil {
|
||||
strongSelf.insertSubnode(shimmerNode, belowSubnode: strongSelf.separatorNode)
|
||||
} else {
|
||||
strongSelf.addSubnode(shimmerNode)
|
||||
}
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
let titleLineWidth: CGFloat = (shimmeringIndex % 2 == 0) ? 120.0 : 80.0
|
||||
let lineDiameter: CGFloat = 8.0
|
||||
|
||||
let titleFrame = strongSelf.titleNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||
|
||||
shimmerNode.update(backgroundColor: item.theme.list.itemBlocksBackgroundColor, foregroundColor: item.theme.list.mediaPlaceholderColor, shimmeringColor: item.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: contentSize)
|
||||
} else if let shimmerNode = strongSelf.placeholderNode {
|
||||
strongSelf.placeholderNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ enum BotReceiptEntry: ItemListNodeEntry {
|
||||
case let .header(theme, invoice, botName):
|
||||
return BotCheckoutHeaderItem(account: arguments.account, theme: theme, invoice: invoice, botName: botName, sectionId: self.section)
|
||||
case let .price(_, theme, text, value, hasSeparator, isFinal):
|
||||
return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, hasSeparator: hasSeparator, sectionId: self.section)
|
||||
return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, hasSeparator: hasSeparator, shimmeringIndex: nil, sectionId: self.section)
|
||||
case let .paymentMethod(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: nil)
|
||||
case let .shippingInfo(_, text, value):
|
||||
@ -301,7 +301,7 @@ final class BotReceiptControllerNode: ItemListControllerNode {
|
||||
self.actionButtonPanelSeparator = ASDisplayNode()
|
||||
self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
self.actionButton = BotCheckoutActionButton(inactiveFillColor: self.presentationData.theme.list.plainBackgroundColor, activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.plainBackgroundColor)
|
||||
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.plainBackgroundColor)
|
||||
self.actionButton.setState(.active(self.presentationData.strings.Common_Done))
|
||||
|
||||
super.init(controller: controller, navigationBar: navigationBar, updateNavigationOffset: updateNavigationOffset, state: signal)
|
||||
@ -338,7 +338,7 @@ final class BotReceiptControllerNode: ItemListControllerNode {
|
||||
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: bottomPanelHorizontalInset, y: bottomPanelVerticalInset), size: CGSize(width: layout.size.width - bottomPanelHorizontalInset * 2.0, height: BotCheckoutActionButton.height))
|
||||
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)
|
||||
self.actionButton.updateLayout(size: actionButtonFrame.size, transition: transition)
|
||||
self.actionButton.updateLayout(absoluteRect: actionButtonFrame.offsetBy(dx: self.actionButtonPanelNode.frame.minX, dy: self.actionButtonPanelNode.frame.minY), containerSize: layout.size, transition: transition)
|
||||
|
||||
updatedInsets.bottom = bottomPanelHeight
|
||||
|
||||
|
@ -166,6 +166,7 @@ public final class PinchSourceContainerNode: ASDisplayNode, UIGestureRecognizerD
|
||||
|
||||
public var activate: ((PinchSourceContainerNode) -> Void)?
|
||||
public var scaleUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
public var animatedOut: (() -> Void)?
|
||||
var deactivate: (() -> Void)?
|
||||
var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
|
||||
|
||||
@ -350,6 +351,8 @@ private final class PinchControllerNode: ViewControllerTracingNode {
|
||||
strongSelf.sourceNode.restoreToNaturalSize()
|
||||
strongSelf.sourceNode.addSubnode(strongSelf.sourceNode.contentNode)
|
||||
|
||||
strongSelf.sourceNode.animatedOut?()
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ swift_library(
|
||||
"//submodules/SegmentedControlNode:SegmentedControlNode",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/AnimationUI:AnimationUI",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -4,6 +4,7 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ShimmerEffect
|
||||
|
||||
public enum ItemListDisclosureItemTitleColor {
|
||||
case primary
|
||||
@ -38,8 +39,9 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
||||
let action: (() -> Void)?
|
||||
let clearHighlightAutomatically: Bool
|
||||
public let tag: ItemListItemTag?
|
||||
public let shimmeringIndex: Int?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.icon = icon
|
||||
self.title = title
|
||||
@ -53,6 +55,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
||||
self.action = action
|
||||
self.clearHighlightAutomatically = clearHighlightAutomatically
|
||||
self.tag = tag
|
||||
self.shimmeringIndex = shimmeringIndex
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -132,6 +135,9 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
@ -180,6 +186,15 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.addSubnode(self.activateArea)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.placeholderNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: ItemListDisclosureItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
@ -479,6 +494,38 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
}
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel))
|
||||
|
||||
if let shimmeringIndex = item.shimmeringIndex {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.placeholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.placeholderNode = shimmerNode
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.insertSubnode(shimmerNode, aboveSubnode: strongSelf.backgroundNode)
|
||||
} else {
|
||||
strongSelf.addSubnode(shimmerNode)
|
||||
}
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
let titleLineWidth: CGFloat = (shimmeringIndex % 2 == 0) ? 120.0 : 80.0
|
||||
let lineDiameter: CGFloat = 8.0
|
||||
|
||||
let titleFrame = strongSelf.titleNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||
|
||||
shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: contentSize)
|
||||
} else if let shimmerNode = strongSelf.placeholderNode {
|
||||
strongSelf.placeholderNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -521,12 +521,14 @@ public final class PrincipalThemeAdditionalGraphics {
|
||||
public let chatBubbleActionButtonIncomingShareIconImage: UIImage
|
||||
public let chatBubbleActionButtonIncomingPhoneIconImage: UIImage
|
||||
public let chatBubbleActionButtonIncomingLocationIconImage: UIImage
|
||||
public let chatBubbleActionButtonIncomingPaymentIconImage: UIImage
|
||||
|
||||
public let chatBubbleActionButtonOutgoingMessageIconImage: UIImage
|
||||
public let chatBubbleActionButtonOutgoingLinkIconImage: UIImage
|
||||
public let chatBubbleActionButtonOutgoingShareIconImage: UIImage
|
||||
public let chatBubbleActionButtonOutgoingPhoneIconImage: UIImage
|
||||
public let chatBubbleActionButtonOutgoingLocationIconImage: UIImage
|
||||
public let chatBubbleActionButtonOutgoingPaymentIconImage: UIImage
|
||||
|
||||
public let chatEmptyItemLockIcon: UIImage
|
||||
public let emptyChatListCheckIcon: UIImage
|
||||
@ -565,11 +567,13 @@ public final class PrincipalThemeAdditionalGraphics {
|
||||
self.chatBubbleActionButtonIncomingShareIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotShare"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonIncomingPhoneIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotPhone"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonIncomingLocationIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLocation"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonIncomingPaymentIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotPayment"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingMessageIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotMessage"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingLinkIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingShareIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotShare"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingPhoneIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotPhone"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingLocationIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLocation"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
self.chatBubbleActionButtonOutgoingPaymentIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotPayment"), color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsTextColor, wallpaper: wallpaper))!
|
||||
|
||||
self.chatEmptyItemLockIcon = generateImage(CGSize(width: 9.0, height: 13.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Message/BotPayment.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Message/BotPayment.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "card.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/BotPayment.imageset/card.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/BotPayment.imageset/card.pdf
vendored
Normal file
Binary file not shown.
@ -1873,7 +1873,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let receiptMessageId = invoice.receiptMessageId {
|
||||
strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: receiptMessageId), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
} else {
|
||||
strongSelf.present(BotCheckoutController(context: strongSelf.context, invoice: invoice, messageId: messageId), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
let inputData = Promise<BotCheckoutController.InputData?>()
|
||||
inputData.set(BotCheckoutController.InputData.fetch(context: strongSelf.context, messageId: message.id)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
|
||||
return .single(nil)
|
||||
})
|
||||
strongSelf.present(BotCheckoutController(context: strongSelf.context, invoice: invoice, messageId: messageId, inputData: inputData), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,8 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
||||
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingLocationIconImage : graphics.chatBubbleActionButtonOutgoingLinkIconImage
|
||||
case .switchInline:
|
||||
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingShareIconImage : graphics.chatBubbleActionButtonOutgoingLinkIconImage
|
||||
case .payment:
|
||||
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingPaymentIconImage : graphics.chatBubbleActionButtonOutgoingPaymentIconImage
|
||||
default:
|
||||
iconImage = nil
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import RadialStatusNode
|
||||
import TelegramUIPreferences
|
||||
import PeerInfoAvatarListNode
|
||||
import AnimationUI
|
||||
import ContextUI
|
||||
|
||||
enum PeerInfoHeaderButtonKey: Hashable {
|
||||
case message
|
||||
@ -771,6 +772,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
|
||||
final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
private let isSettings: Bool
|
||||
let pinchSourceNode: PinchSourceContainerNode
|
||||
let avatarContainerNode: PeerInfoAvatarTransformContainerNode
|
||||
let listContainerTransformNode: ASDisplayNode
|
||||
let listContainerNode: PeerInfoAvatarListContainerNode
|
||||
@ -781,10 +783,13 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
var item: PeerInfoAvatarListItem?
|
||||
|
||||
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
|
||||
var animateOverlaysFadeIn: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, readyWhenGalleryLoads: Bool, isSettings: Bool) {
|
||||
self.isSettings = isSettings
|
||||
|
||||
self.pinchSourceNode = PinchSourceContainerNode()
|
||||
|
||||
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
|
||||
self.listContainerTransformNode = ASDisplayNode()
|
||||
self.listContainerNode = PeerInfoAvatarListContainerNode(context: context)
|
||||
@ -793,9 +798,10 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.avatarContainerNode)
|
||||
self.addSubnode(self.pinchSourceNode)
|
||||
self.pinchSourceNode.contentNode.addSubnode(self.avatarContainerNode)
|
||||
self.listContainerTransformNode.addSubnode(self.listContainerNode)
|
||||
self.addSubnode(self.listContainerTransformNode)
|
||||
self.pinchSourceNode.contentNode.addSubnode(self.listContainerTransformNode)
|
||||
|
||||
let avatarReady = (self.avatarContainerNode.avatarNode.ready
|
||||
|> mapToSignal { _ -> Signal<Bool, NoError> in
|
||||
@ -837,10 +843,29 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.pinchSourceNode.activate = { [weak self] sourceNode in
|
||||
guard let _ = self else {
|
||||
return
|
||||
}
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
}
|
||||
|
||||
self.pinchSourceNode.animatedOut = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.animateOverlaysFadeIn?()
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
|
||||
self.arguments = (peer, theme, avatarSize, isExpanded)
|
||||
self.pinchSourceNode.update(size: size, transition: transition)
|
||||
self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.avatarContainerNode.update(peer: peer, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings)
|
||||
}
|
||||
|
||||
@ -1634,6 +1659,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
var requestOpenAvatarForEditing: ((Bool) -> Void)?
|
||||
var cancelUpload: (() -> Void)?
|
||||
var requestUpdateLayout: (() -> Void)?
|
||||
var animateOverlaysFadeIn: (() -> Void)?
|
||||
|
||||
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
||||
@ -1748,6 +1774,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
strongSelf.editingContentNode.avatarNode.update(peer: peer, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
|
||||
}
|
||||
|
||||
self.avatarListNode.animateOverlaysFadeIn = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.navigationButtonContainer.layer.animateAlpha(from: 0.0, to: strongSelf.navigationButtonContainer.alpha, duration: 0.25)
|
||||
strongSelf.avatarListNode.listContainerNode.shadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.shadowNode.alpha, duration: 0.25)
|
||||
strongSelf.avatarListNode.listContainerNode.controlsContainerNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.controlsContainerNode.alpha, duration: 0.25)
|
||||
|
||||
strongSelf.animateOverlaysFadeIn?()
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
@ -2390,6 +2390,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
}
|
||||
|
||||
self.headerNode.animateOverlaysFadeIn = { [weak self] in
|
||||
guard let strongSelf = self, let navigationBar = strongSelf.controller?.navigationBar else {
|
||||
return
|
||||
}
|
||||
navigationBar.layer.animateAlpha(from: 0.0, to: navigationBar.alpha, duration: 0.25)
|
||||
}
|
||||
|
||||
self.headerNode.requestUpdateLayout = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user