mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-12 01:10:49 +00:00
298 lines
13 KiB
Swift
298 lines
13 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import AccountContext
|
|
import ComponentFlow
|
|
import MultilineTextComponent
|
|
import BundleIconComponent
|
|
import ButtonComponent
|
|
import GiftItemComponent
|
|
import AnimatedTextComponent
|
|
|
|
private let titleFont = Font.semibold(15.0)
|
|
private let subtitleFont = Font.regular(14.0)
|
|
|
|
final class GiftAuctionAccessoryPanel: ASDisplayNode {
|
|
private let context: AccountContext
|
|
private var theme: PresentationTheme
|
|
private var strings: PresentationStrings
|
|
|
|
private let tapAction: () -> Void
|
|
|
|
private let contentNode: ASDisplayNode
|
|
|
|
private let title = ComponentView<Empty>()
|
|
private let subtitle = ComponentView<Empty>()
|
|
private let button = ComponentView<Empty>()
|
|
|
|
private let separatorNode: ASDisplayNode
|
|
|
|
private var validLayout: (CGSize, CGFloat, CGFloat, Bool)?
|
|
private var states: [GiftAuctionContext.State] = []
|
|
private var giftAuctionTimer: SwiftSignalKit.Timer?
|
|
|
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, tapAction: @escaping () -> Void) {
|
|
self.context = context
|
|
self.theme = theme
|
|
self.strings = strings
|
|
|
|
self.tapAction = tapAction
|
|
|
|
self.contentNode = ASDisplayNode()
|
|
|
|
self.separatorNode = ASDisplayNode()
|
|
self.separatorNode.isLayerBacked = true
|
|
self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor
|
|
|
|
super.init()
|
|
|
|
self.clipsToBounds = true
|
|
|
|
self.addSubnode(self.contentNode)
|
|
|
|
self.contentNode.addSubnode(self.separatorNode)
|
|
|
|
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
|
if let self, let (size, leftInset, rightInset, isHidden) = self.validLayout {
|
|
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate)
|
|
}
|
|
}, queue: Queue.mainQueue())
|
|
self.giftAuctionTimer?.start()
|
|
}
|
|
|
|
deinit {
|
|
self.giftAuctionTimer?.invalidate()
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
|
self.view.addGestureRecognizer(tapRecognizer)
|
|
}
|
|
|
|
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) {
|
|
self.validLayout = (size, leftInset, rightInset, isHidden)
|
|
|
|
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size))
|
|
transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0)
|
|
|
|
|
|
guard self.states.count > 0 else {
|
|
return
|
|
}
|
|
|
|
var titleItems: [AnyComponentWithIdentity<Empty>] = []
|
|
for auctionState in self.states {
|
|
if case let .generic(gift) = auctionState.gift {
|
|
titleItems.append(AnyComponentWithIdentity(id: "icon-\(gift.id)", component: AnyComponent(
|
|
GiftItemComponent(
|
|
context: self.context,
|
|
theme: self.theme,
|
|
strings: self.strings,
|
|
peer: nil,
|
|
subject: .starGift(gift: gift, price: ""),
|
|
mode: .tableIcon
|
|
)
|
|
)))
|
|
}
|
|
}
|
|
|
|
let titleText: String = self.strings.ChatList_Auctions_ActiveAuction(Int32(self.states.count))
|
|
titleItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: titleText, font: titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor))))))
|
|
|
|
let titleSize = self.title.update(
|
|
transition: .immediate,
|
|
component: AnyComponent(
|
|
HStack(titleItems, spacing: 3.0, alignment: .left)
|
|
),
|
|
environment: {},
|
|
containerSize: size
|
|
)
|
|
let titleFrame = CGRect(origin: CGPoint(x: 16.0, y: 9.0), size: titleSize)
|
|
if let titleView = self.title.view {
|
|
if titleView.superview == nil {
|
|
self.contentNode.view.addSubview(titleView)
|
|
}
|
|
titleView.frame = titleFrame
|
|
}
|
|
|
|
let subtitleText: String
|
|
var subtitleTextColor = self.theme.rootController.navigationBar.secondaryTextColor
|
|
var isOutbid = false
|
|
|
|
var buttonAnimatedTitleItems: [AnimatedTextComponent.Item] = []
|
|
|
|
if self.states.count == 1, let auctionState = self.states.first {
|
|
let place = auctionState.place ?? 1
|
|
if case let .generic(gift) = auctionState.gift, let auctionGiftsPerRound = gift.auctionGiftsPerRound, place > auctionGiftsPerRound {
|
|
subtitleText = self.strings.ChatList_Auctions_Status_Single_Outbid
|
|
subtitleTextColor = self.theme.list.itemDestructiveColor
|
|
isOutbid = true
|
|
} else {
|
|
let placeText: String
|
|
let lastDigit = place % 10
|
|
switch lastDigit {
|
|
case 1:
|
|
placeText = self.strings.ChatList_Auctions_Status_Single_PlaceFirst("\(place)").string
|
|
case 2:
|
|
placeText = self.strings.ChatList_Auctions_Status_Single_PlaceSecond("\(place)").string
|
|
case 3:
|
|
placeText = self.strings.ChatList_Auctions_Status_Single_PlaceThird("\(place)").string
|
|
default:
|
|
placeText = self.strings.ChatList_Auctions_Status_Single_PlaceNTh("\(place)").string
|
|
}
|
|
subtitleText = self.strings.ChatList_Auctions_Status_Single_Winning(placeText).string
|
|
}
|
|
|
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
|
var endTime = currentTime
|
|
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, _, _) = auctionState.auctionState {
|
|
endTime = nextRoundDate
|
|
}
|
|
|
|
let endTimeout = max(0, endTime - currentTime)
|
|
|
|
let hours = Int(endTimeout / 3600)
|
|
let minutes = Int((endTimeout % 3600) / 60)
|
|
let seconds = Int(endTimeout % 60)
|
|
|
|
if hours > 0 {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "h", content: .number(hours, minDigits: 1)))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "colon1", content: .text(":")))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "colon2", content: .text(":")))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
|
} else {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "colon2", content: .text(":")))
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
|
}
|
|
} else {
|
|
var outbidCount = 0
|
|
for auctionState in self.states {
|
|
let place = auctionState.place ?? 1
|
|
if case let .generic(gift) = auctionState.gift, let auctionGiftsPerRound = gift.auctionGiftsPerRound, place > auctionGiftsPerRound {
|
|
outbidCount += 1
|
|
}
|
|
}
|
|
if outbidCount > 0 {
|
|
if outbidCount == self.states.count {
|
|
subtitleText = self.strings.ChatList_Auctions_Status_Many_OutbidAll
|
|
} else {
|
|
subtitleText = self.strings.ChatList_Auctions_Status_Many_Outbid(Int32(outbidCount))
|
|
}
|
|
subtitleTextColor = self.theme.list.itemDestructiveColor
|
|
isOutbid = true
|
|
} else {
|
|
subtitleText = self.strings.ChatList_Auctions_Status_Many_WinningAll
|
|
}
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "view", content: .text(self.strings.ChatList_Auctions_View)))
|
|
}
|
|
|
|
let subtitleSize = self.subtitle.update(
|
|
transition: .immediate,
|
|
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: subtitleText, font: subtitleFont, textColor: subtitleTextColor)))),
|
|
environment: {},
|
|
containerSize: size
|
|
)
|
|
let subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: 29.0), size: subtitleSize)
|
|
if let subtitleView = self.subtitle.view {
|
|
if subtitleView.superview == nil {
|
|
self.contentNode.view.addSubview(subtitleView)
|
|
}
|
|
subtitleView.frame = subtitleFrame
|
|
}
|
|
|
|
let buttonSize = self.button.update(
|
|
transition: .spring(duration: 0.2),
|
|
component: AnyComponent(
|
|
ButtonComponent(
|
|
background: ButtonComponent.Background(
|
|
color: self.theme.list.itemCheckColors.fillColor,
|
|
foreground: self.theme.list.itemCheckColors.foregroundColor,
|
|
pressedColor: self.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
|
cornerRadius: 14.0,
|
|
isShimmering: isOutbid
|
|
),
|
|
content: AnyComponentWithIdentity(
|
|
id: "content",
|
|
component: AnyComponent(HStack([
|
|
AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Premium/Auction/BidSmall", tintColor: self.theme.list.itemCheckColors.foregroundColor))),
|
|
AnyComponentWithIdentity(id: "timer", component: AnyComponent(
|
|
AnimatedTextComponent(
|
|
font: Font.with(size: 15.0, weight: .semibold, traits: .monospacedNumbers),
|
|
color: self.theme.list.itemCheckColors.foregroundColor,
|
|
items: buttonAnimatedTitleItems,
|
|
noDelay: true
|
|
)
|
|
))
|
|
], spacing: 3.0))
|
|
),
|
|
fitToContentWidth: true,
|
|
action: { [weak self] in
|
|
guard let self else {
|
|
return
|
|
}
|
|
self.tapAction()
|
|
}
|
|
)
|
|
),
|
|
environment: {},
|
|
containerSize: CGSize(width: size.width, height: 28.0)
|
|
)
|
|
let buttonFrame = CGRect(origin: CGPoint(x: size.width - rightInset - buttonSize.width - 16.0, y: 14.0), size: buttonSize)
|
|
if let buttonView = self.button.view {
|
|
if buttonView.superview == nil {
|
|
self.contentNode.view.addSubview(buttonView)
|
|
}
|
|
buttonView.frame = buttonFrame
|
|
}
|
|
|
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)))
|
|
}
|
|
|
|
func update(states: [GiftAuctionContext.State]) {
|
|
self.states = states
|
|
if let (size, leftInset, rightInset, isHidden) = self.validLayout {
|
|
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate)
|
|
}
|
|
}
|
|
|
|
func animateIn(_ transition: ContainedViewLayoutTransition) {
|
|
let contentPosition = self.contentNode.layer.position
|
|
|
|
transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 56.0))
|
|
|
|
guard let (size, _, _, _) = self.validLayout else {
|
|
return
|
|
}
|
|
|
|
transition.animatePositionAdditive(node: self.separatorNode, offset: CGPoint(x: 0.0, y: size.height))
|
|
}
|
|
|
|
func animateOut(_ transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
|
let contentPosition = self.contentNode.layer.position
|
|
transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 56.0), removeOnCompletion: false, completion: { _ in
|
|
completion()
|
|
})
|
|
|
|
guard let (size, _, _, _) = self.validLayout else {
|
|
return
|
|
}
|
|
|
|
transition.updatePosition(node: self.separatorNode, position: self.separatorNode.position.offsetBy(dx: 0.0, dy: size.height))
|
|
}
|
|
|
|
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
|
if case .ended = recognizer.state {
|
|
self.tapAction()
|
|
}
|
|
}
|
|
}
|