mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-01 04:08:07 +00:00
1145 lines
59 KiB
Swift
1145 lines
59 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import AccountContext
|
|
import TelegramPresentationData
|
|
import PresentationDataUtils
|
|
import ComponentFlow
|
|
import ViewControllerComponent
|
|
import SheetComponent
|
|
import MultilineTextComponent
|
|
import MultilineTextWithEntitiesComponent
|
|
import BundleIconComponent
|
|
import Markdown
|
|
import BalancedTextComponent
|
|
import TextFormat
|
|
import TelegramStringFormatting
|
|
import PlainButtonComponent
|
|
import TooltipUI
|
|
import GiftAnimationComponent
|
|
import ContextUI
|
|
import GiftItemComponent
|
|
import GlassBarButtonComponent
|
|
import ButtonComponent
|
|
import UndoUI
|
|
import LottieComponent
|
|
import AnimatedTextComponent
|
|
|
|
private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
|
|
|
let context: AccountContext
|
|
let auctionContext: GiftAuctionContext
|
|
let animateOut: ActionSlot<Action<()>>
|
|
let getController: () -> ViewController?
|
|
|
|
init(
|
|
context: AccountContext,
|
|
auctionContext: GiftAuctionContext,
|
|
animateOut: ActionSlot<Action<()>>,
|
|
getController: @escaping () -> ViewController?
|
|
) {
|
|
self.context = context
|
|
self.auctionContext = auctionContext
|
|
self.animateOut = animateOut
|
|
self.getController = getController
|
|
}
|
|
|
|
static func ==(lhs: GiftAuctionViewSheetContent, rhs: GiftAuctionViewSheetContent) -> Bool {
|
|
if lhs.context !== rhs.context {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
final class State: ComponentState {
|
|
let averagePriceTag = GenericComponentViewTag()
|
|
|
|
private let context: AccountContext
|
|
private let auctionContext: GiftAuctionContext
|
|
private let animateOut: ActionSlot<Action<()>>
|
|
private let getController: () -> ViewController?
|
|
|
|
private var disposable: Disposable?
|
|
private(set) var auctionState: GiftAuctionContext.State?
|
|
|
|
private var giftAuctionTimer: SwiftSignalKit.Timer?
|
|
fileprivate var giftAuctionAcquiredGifts: [GiftAuctionAcquiredGift] = []
|
|
private var giftAuctionAcquiredGiftsDisposable: Disposable?
|
|
|
|
var cachedStarImage: (UIImage, PresentationTheme)?
|
|
|
|
var cachedChevronImage: (UIImage, PresentationTheme)?
|
|
var cachedSmallChevronImage: (UIImage, PresentationTheme)?
|
|
|
|
init(
|
|
context: AccountContext,
|
|
auctionContext: GiftAuctionContext,
|
|
animateOut: ActionSlot<Action<()>>,
|
|
getController: @escaping () -> ViewController?
|
|
) {
|
|
self.context = context
|
|
self.auctionContext = auctionContext
|
|
self.animateOut = animateOut
|
|
self.getController = getController
|
|
|
|
super.init()
|
|
|
|
self.disposable = (auctionContext.state
|
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|
guard let self else {
|
|
return
|
|
}
|
|
self.auctionState = state
|
|
self.updated()
|
|
})
|
|
|
|
self.giftAuctionAcquiredGiftsDisposable = (context.engine.payments.getGiftAuctionAcquiredGifts(giftId: auctionContext.gift.giftId)
|
|
|> deliverOnMainQueue).startStrict(next: { [weak self] acquiredGifts in
|
|
guard let self else {
|
|
return
|
|
}
|
|
self.giftAuctionAcquiredGifts = acquiredGifts
|
|
self.updated(transition: .easeInOut(duration: 0.25))
|
|
})
|
|
|
|
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
|
self?.updated()
|
|
}, queue: Queue.mainQueue())
|
|
self.giftAuctionTimer?.start()
|
|
}
|
|
|
|
deinit {
|
|
self.disposable?.dispose()
|
|
self.giftAuctionAcquiredGiftsDisposable?.dispose()
|
|
self.giftAuctionTimer?.invalidate()
|
|
}
|
|
|
|
func showAttributeInfo(tag: Any, text: String) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
|
return
|
|
}
|
|
controller.dismissAllTooltips()
|
|
|
|
guard let sourceView = controller.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else {
|
|
return
|
|
}
|
|
|
|
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
|
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .markdown(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
|
return .dismiss(consume: false)
|
|
})
|
|
controller.present(tooltipController, in: .current)
|
|
}
|
|
|
|
func openGiftResale(gift: StarGift.Gift) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
|
return
|
|
}
|
|
let storeController = self.context.sharedContext.makeGiftStoreController(
|
|
context: self.context,
|
|
peerId: self.context.account.peerId,
|
|
gift: gift
|
|
)
|
|
controller.push(storeController)
|
|
}
|
|
|
|
func openGiftFragmentResale(url: String) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController else {
|
|
return
|
|
}
|
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
|
}
|
|
|
|
func openAuction() {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController else {
|
|
return
|
|
}
|
|
|
|
self.dismiss(animated: true)
|
|
|
|
let bidController = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, auctionContext: self.auctionContext)
|
|
navigationController.pushViewController(bidController)
|
|
}
|
|
|
|
func openPeer(_ peer: EnginePeer, dismiss: Bool = true) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController else {
|
|
return
|
|
}
|
|
|
|
controller.dismissAllTooltips()
|
|
|
|
let context = self.context
|
|
let action = {
|
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
|
navigationController: navigationController,
|
|
chatController: nil,
|
|
context: context,
|
|
chatLocation: .peer(peer),
|
|
subject: nil,
|
|
botStart: nil,
|
|
updateTextInputState: nil,
|
|
keepStack: .always,
|
|
useExisting: true,
|
|
purposefulAction: nil,
|
|
scrollToEndIfExists: false,
|
|
activateMessageSearch: nil,
|
|
animated: true
|
|
))
|
|
}
|
|
|
|
if dismiss {
|
|
self.dismiss(animated: true)
|
|
Queue.mainQueue().after(0.4, {
|
|
action()
|
|
})
|
|
} else {
|
|
action()
|
|
}
|
|
}
|
|
|
|
func share() {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
|
return
|
|
}
|
|
|
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
var link = ""
|
|
if case let .generic(gift) = self.auctionContext.gift, let slug = gift.auctionSlug {
|
|
link = "https://t.me/auction/\(slug)"
|
|
}
|
|
|
|
let shareController = self.context.sharedContext.makeShareController(
|
|
context: self.context,
|
|
subject: .url(link),
|
|
forceExternal: false,
|
|
shareStory: nil,
|
|
enqueued: { [weak self, weak controller] peerIds, _ in
|
|
guard let self else {
|
|
return
|
|
}
|
|
let _ = (self.context.engine.data.get(
|
|
EngineDataList(
|
|
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
|
)
|
|
)
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak controller] peerList in
|
|
guard let self else {
|
|
return
|
|
}
|
|
let peers = peerList.compactMap { $0 }
|
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
let text: String
|
|
var savedMessages = false
|
|
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
|
|
text = presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One
|
|
savedMessages = true
|
|
} else {
|
|
if peers.count == 1, let peer = peers.first {
|
|
var peerName = peer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
|
text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
|
|
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
|
var firstPeerName = firstPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
|
var secondPeerName = secondPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
|
text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
|
} else if let peer = peers.first {
|
|
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
|
text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
|
} else {
|
|
text = ""
|
|
}
|
|
}
|
|
|
|
controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { [weak self, weak controller] action in
|
|
if let self, savedMessages, action == .info {
|
|
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
|
|> deliverOnMainQueue).start(next: { [weak self, weak controller] peer in
|
|
guard let peer else {
|
|
return
|
|
}
|
|
self?.openPeer(peer)
|
|
Queue.mainQueue().after(0.6) {
|
|
controller?.dismiss(animated: false, completion: nil)
|
|
}
|
|
})
|
|
}
|
|
return false
|
|
}, additionalView: nil), in: .current)
|
|
})
|
|
},
|
|
actionCompleted: { [weak controller] in
|
|
controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
|
}
|
|
)
|
|
controller.present(shareController, in: .window(.root))
|
|
}
|
|
|
|
func morePressed(view: UIView, gesture: ContextGesture?) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
|
return
|
|
}
|
|
|
|
let context = self.context
|
|
let gift = self.auctionContext.gift
|
|
let auctionContext = self.auctionContext
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
var link = ""
|
|
if case let .generic(gift) = gift, let slug = gift.auctionSlug {
|
|
link = "https://t.me/auction/\(slug)"
|
|
}
|
|
|
|
var items: [ContextMenuItem] = []
|
|
|
|
if let auctionState = self.auctionState, case .ongoing = auctionState.auctionState {
|
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_Auction_Context_About, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, f in
|
|
f(.default)
|
|
|
|
let infoController = context.sharedContext.makeGiftAuctionInfoScreen(context: context, auctionContext: auctionContext, completion: nil)
|
|
controller?.push(infoController)
|
|
})))
|
|
}
|
|
|
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_Auction_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, f in
|
|
f(.default)
|
|
|
|
UIPasteboard.general.string = link
|
|
|
|
controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
|
})))
|
|
|
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_Auction_Context_Share, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
|
f(.default)
|
|
|
|
self?.share()
|
|
})))
|
|
|
|
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
|
controller.presentInGlobalOverlay(contextController)
|
|
}
|
|
|
|
func dismiss(animated: Bool) {
|
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
|
return
|
|
}
|
|
if animated {
|
|
controller.dismissAllTooltips()
|
|
self.animateOut.invoke(Action { [weak controller] _ in
|
|
controller?.dismiss(completion: nil)
|
|
})
|
|
} else {
|
|
controller.dismiss(animated: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeState() -> State {
|
|
return State(context: self.context, auctionContext: self.auctionContext, animateOut: self.animateOut, getController: self.getController)
|
|
}
|
|
|
|
static var body: Body {
|
|
let closeButton = Child(GlassBarButtonComponent.self)
|
|
let moreButton = Child(GlassBarButtonComponent.self)
|
|
let animation = Child(GiftItemComponent.self)
|
|
|
|
let title = Child(MultilineTextComponent.self)
|
|
let description = Child(BalancedTextComponent.self)
|
|
|
|
let table = Child(TableComponent.self)
|
|
|
|
let button = Child(ButtonComponent.self)
|
|
|
|
let acquiredButton = Child(PlainButtonComponent.self)
|
|
// let telegramSaleButton = Child(PlainButtonComponent.self)
|
|
// let fragmentSaleButton = Child(PlainButtonComponent.self)
|
|
|
|
let moreButtonPlayOnce = ActionSlot<Void>()
|
|
|
|
return { context in
|
|
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
|
|
|
let component = context.component
|
|
let theme = environment.theme
|
|
let strings = environment.strings
|
|
let dateTimeFormat = environment.dateTimeFormat
|
|
|
|
let state = context.state
|
|
|
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
|
|
|
var titleString: String = ""
|
|
var giftIconSubject: GiftItemComponent.Subject?
|
|
var genericGift: StarGift.Gift?
|
|
|
|
switch component.auctionContext.gift {
|
|
case let .generic(gift):
|
|
titleString = gift.title ?? ""
|
|
giftIconSubject = .starGift(gift: gift, price: "")
|
|
genericGift = gift
|
|
default:
|
|
break
|
|
}
|
|
|
|
let _ = giftIconSubject
|
|
let _ = genericGift
|
|
|
|
var originY: CGFloat = 0.0
|
|
|
|
if let genericGift {
|
|
let animation = animation.update(
|
|
component: GiftItemComponent(
|
|
context: component.context,
|
|
theme: environment.theme,
|
|
strings: environment.strings,
|
|
subject: .starGift(gift: genericGift, price: ""),
|
|
ribbon: GiftItemComponent.Ribbon(text: strings.Gift_Auction_Auction, color: .orange),
|
|
outline: .orange,
|
|
mode: .header
|
|
),
|
|
availableSize: CGSize(width: 120.0, height: 120.0),
|
|
transition: context.transition
|
|
)
|
|
context.add(animation
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: 92.0))
|
|
)
|
|
}
|
|
originY += 177.0
|
|
|
|
let title = title.update(
|
|
component: MultilineTextComponent(
|
|
text: .plain(NSAttributedString(
|
|
string: titleString,
|
|
font: Font.bold(24.0),
|
|
textColor: theme.list.itemPrimaryTextColor,
|
|
paragraphAlignment: .center
|
|
)),
|
|
horizontalAlignment: .center,
|
|
maximumNumberOfLines: 1
|
|
),
|
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
|
transition: .immediate
|
|
)
|
|
context.add(title
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: 174.0))
|
|
)
|
|
|
|
var descriptionText: String = ""
|
|
var descriptionColor = theme.list.itemSecondaryTextColor
|
|
|
|
let tableFont = Font.regular(15.0)
|
|
let tableTextColor = theme.list.itemPrimaryTextColor
|
|
|
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
|
var endTime = currentTime
|
|
|
|
var isEnded = false
|
|
var tableItems: [TableComponent.Item] = []
|
|
if let auctionState = state.auctionState, case let .generic(gift) = component.auctionContext.gift {
|
|
endTime = auctionState.endDate
|
|
if case .finished = auctionState.auctionState {
|
|
isEnded = true
|
|
} else if auctionState.endDate < currentTime {
|
|
isEnded = true
|
|
}
|
|
|
|
if isEnded {
|
|
descriptionText = strings.Gift_Auction_Ended
|
|
descriptionColor = theme.list.itemDestructiveColor
|
|
|
|
tableItems.append(.init(
|
|
id: "firstSale",
|
|
title: strings.Gift_Auction_FirstSale,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.startDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
tableItems.append(.init(
|
|
id: "lastSale",
|
|
title: strings.Gift_Auction_LastSale,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.endDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
if case let .finished(_, _, averagePrice) = auctionState.auctionState {
|
|
var items: [AnyComponentWithIdentity<Empty>] = []
|
|
|
|
let valueString = "\(presentationStringsFormattedNumber(abs(Int32(clamping: averagePrice)), dateTimeFormat.groupingSeparator))⭐️"
|
|
let valueAttributedString = NSMutableAttributedString(string: valueString, font: tableFont, textColor: tableTextColor)
|
|
let range = (valueAttributedString.string as NSString).range(of: "⭐️")
|
|
if range.location != NSNotFound {
|
|
valueAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: range)
|
|
valueAttributedString.addAttribute(.baselineOffset, value: 1.0, range: range)
|
|
}
|
|
|
|
let averagePriceString = strings.Gift_Auction_Stars(Int32(clamping: averagePrice))
|
|
items.append(AnyComponentWithIdentity(id: "value", component: AnyComponent(
|
|
MultilineTextWithEntitiesComponent(
|
|
context: component.context,
|
|
animationCache: component.context.animationCache,
|
|
animationRenderer: component.context.animationRenderer,
|
|
placeholderColor: theme.list.mediaPlaceholderColor,
|
|
text: .plain(valueAttributedString),
|
|
maximumNumberOfLines: 0
|
|
)
|
|
)))
|
|
items.append(AnyComponentWithIdentity(
|
|
id: AnyHashable(1),
|
|
component: AnyComponent(Button(
|
|
content: AnyComponent(ButtonContentComponent(
|
|
context: component.context,
|
|
text: "?",
|
|
color: theme.list.itemAccentColor
|
|
)),
|
|
action: { [weak state] in
|
|
guard let state else {
|
|
return
|
|
}
|
|
state.showAttributeInfo(tag: state.averagePriceTag, text: strings.Gift_Auction_AveragePriceInfo(averagePriceString, titleString).string)
|
|
}
|
|
).tagged(state.averagePriceTag))
|
|
))
|
|
|
|
tableItems.append(.init(
|
|
id: "averagePrice",
|
|
title: strings.Gift_Auction_AveragePrice,
|
|
component: AnyComponent(HStack(items, spacing: 4.0)),
|
|
insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0)
|
|
))
|
|
}
|
|
tableItems.append(.init(
|
|
id: "availability",
|
|
title: strings.Gift_Auction_Availability,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_AvailabilityOf("0", presentationStringsFormattedNumber(gift.availability?.total ?? 0, dateTimeFormat.groupingSeparator)).string, font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
} else {
|
|
var auctionGiftsPerRound: Int32 = 50
|
|
if let auctionGiftsPerRoundValue = gift.auctionGiftsPerRound {
|
|
auctionGiftsPerRound = auctionGiftsPerRoundValue
|
|
}
|
|
descriptionText = strings.Gift_Auction_Description("\(auctionGiftsPerRound)", gift.title ?? "").string
|
|
|
|
tableItems.append(.init(
|
|
id: "start",
|
|
title: strings.Gift_Auction_Started,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.startDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
tableItems.append(.init(
|
|
id: "ends",
|
|
title: strings.Gift_Auction_Ends,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.endDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
if case let .ongoing(_, _, _, _, _, _, _, giftsLeft, currentRound, totalRounds) = auctionState.auctionState {
|
|
tableItems.append(.init(
|
|
id: "round",
|
|
title: strings.Gift_Auction_CurrentRound,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_Round("\(currentRound)", "\(totalRounds)").string, font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
tableItems.append(.init(
|
|
id: "availability",
|
|
title: strings.Gift_Auction_Availability,
|
|
component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_AvailabilityOf(presentationStringsFormattedNumber(giftsLeft, dateTimeFormat.groupingSeparator), presentationStringsFormattedNumber(gift.availability?.total ?? 0, dateTimeFormat.groupingSeparator)).string, font: tableFont, textColor: tableTextColor)))
|
|
)
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
let textFont = Font.regular(15.0)
|
|
let boldTextFont = Font.semibold(15.0)
|
|
let textColor = descriptionColor
|
|
let linkColor = theme.list.itemAccentColor
|
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
|
return (TelegramTextAttributes.URL, contents)
|
|
})
|
|
|
|
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
|
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
|
}
|
|
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
|
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
|
}
|
|
descriptionText = descriptionText.replacingOccurrences(of: " >]", with: "\u{00A0}>]")
|
|
|
|
let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
|
if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
|
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
|
}
|
|
|
|
let description = description.update(
|
|
component: BalancedTextComponent(
|
|
text: .plain(attributedString),
|
|
maximumNumberOfLines: 0,
|
|
lineSpacing: 0.2,
|
|
highlightColor: linkColor.withAlphaComponent(0.1),
|
|
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
|
|
highlightAction: { attributes in
|
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
|
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
|
} else {
|
|
return nil
|
|
}
|
|
},
|
|
tapAction: { attributes, _ in
|
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
|
let controller = component.context.sharedContext.makeGiftAuctionInfoScreen(
|
|
context: component.context,
|
|
auctionContext: component.auctionContext,
|
|
completion: nil
|
|
)
|
|
environment.controller()?.push(controller)
|
|
}
|
|
}
|
|
),
|
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
|
|
transition: .immediate
|
|
)
|
|
context.add(description
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: 198.0 + description.size.height / 2.0))
|
|
.appear(.default(alpha: true))
|
|
.disappear(.default(alpha: true))
|
|
)
|
|
originY += description.size.height
|
|
originY += 42.0
|
|
|
|
let table = table.update(
|
|
component: TableComponent(
|
|
theme: environment.theme,
|
|
items: tableItems
|
|
),
|
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
|
|
transition: context.transition
|
|
)
|
|
context.add(table
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
|
|
.appear(.default(alpha: true))
|
|
.disappear(.default(alpha: true))
|
|
)
|
|
originY += table.size.height + 26.0
|
|
|
|
var hasAdditionalButtons = false
|
|
if state.giftAuctionAcquiredGifts.count > 0, case let .generic(gift) = component.auctionContext.gift {
|
|
originY += 5.0
|
|
|
|
let acquiredButton = acquiredButton.update(
|
|
component: PlainButtonComponent(content: AnyComponent(
|
|
HStack([
|
|
AnyComponentWithIdentity(id: "count", component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(Int32(state.giftAuctionAcquiredGifts.count), dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
)),
|
|
AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
|
|
Rectangle(color: .clear, width: 8.0, height: 1.0)
|
|
)),
|
|
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
|
GiftItemComponent(
|
|
context: component.context,
|
|
theme: theme,
|
|
strings: strings,
|
|
peer: nil,
|
|
subject: .starGift(gift: gift, price: ""),
|
|
mode: .buttonIcon
|
|
)
|
|
)),
|
|
AnyComponentWithIdentity(id: "text", component: AnyComponent(
|
|
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Auction_ItemsBought(Int32(state.giftAuctionAcquiredGifts.count)))", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
)),
|
|
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
|
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
|
))
|
|
], spacing: 0.0)
|
|
), action: { [weak state] in
|
|
guard let state else {
|
|
return
|
|
}
|
|
let giftController = GiftAuctionAcquiredScreen(context: component.context, gift: component.auctionContext.gift, acquiredGifts: state.giftAuctionAcquiredGifts)
|
|
environment.controller()?.push(giftController)
|
|
}, animateScale: false),
|
|
availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height),
|
|
transition: context.transition
|
|
)
|
|
context.add(acquiredButton
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + acquiredButton.size.height / 2.0)))
|
|
originY += acquiredButton.size.height
|
|
originY += 12.0
|
|
|
|
hasAdditionalButtons = true
|
|
}
|
|
|
|
if hasAdditionalButtons {
|
|
originY += 21.0
|
|
}
|
|
|
|
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
|
let buttonSize = CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
|
let buttonBackground = ButtonComponent.Background(
|
|
style: .glass,
|
|
color: theme.list.itemCheckColors.fillColor,
|
|
foreground: theme.list.itemCheckColors.foregroundColor,
|
|
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
|
)
|
|
|
|
let buttonChild: _UpdatedChildComponent
|
|
if !isEnded {
|
|
let buttonAttributedString = NSMutableAttributedString(string: strings.Gift_Auction_Join, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
|
|
|
let endTimeout = max(0, endTime - currentTime)
|
|
|
|
let hours = Int(endTimeout / 3600)
|
|
let minutes = Int((endTimeout % 3600) / 60)
|
|
let seconds = Int(endTimeout % 60)
|
|
|
|
let rawString = hours > 0 ? strings.Gift_Auction_TimeLeftHours : strings.Gift_Auction_TimeLeftMinutes
|
|
var buttonAnimatedTitleItems: [AnimatedTextComponent.Item] = []
|
|
var startIndex = rawString.startIndex
|
|
while true {
|
|
if let range = rawString.range(of: "{", range: startIndex ..< rawString.endIndex) {
|
|
if range.lowerBound != startIndex {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "prefix", content: .text(String(rawString[startIndex ..< range.lowerBound]))))
|
|
}
|
|
|
|
startIndex = range.upperBound
|
|
if let endRange = rawString.range(of: "}", range: startIndex ..< rawString.endIndex) {
|
|
let controlString = rawString[range.upperBound ..< endRange.lowerBound]
|
|
if controlString == "h" {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "h", content: .number(hours, minDigits: 2)))
|
|
} else if controlString == "m" {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
|
} else if controlString == "s" {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
|
}
|
|
|
|
startIndex = endRange.upperBound
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if startIndex != rawString.endIndex {
|
|
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "suffix", content: .text(String(rawString[startIndex ..< rawString.endIndex]))))
|
|
}
|
|
|
|
let items: [AnyComponentWithIdentity<Empty>] = [
|
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))),
|
|
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(AnimatedTextComponent(
|
|
font: Font.with(size: 12.0, weight: .medium, traits: .monospacedNumbers),
|
|
color: theme.list.itemCheckColors.foregroundColor.withAlphaComponent(0.7),
|
|
items: buttonAnimatedTitleItems,
|
|
noDelay: true
|
|
)))
|
|
]
|
|
|
|
buttonChild = button.update(
|
|
component: ButtonComponent(
|
|
background: buttonBackground,
|
|
content: AnyComponentWithIdentity(
|
|
id: AnyHashable("buy"),
|
|
component: AnyComponent(VStack(items, spacing: 1.0))
|
|
),
|
|
isEnabled: true,
|
|
displaysProgress: false,
|
|
action: { [weak state] in
|
|
guard let state else {
|
|
return
|
|
}
|
|
state.openAuction()
|
|
}),
|
|
availableSize: buttonSize,
|
|
transition: .spring(duration: 0.2)
|
|
)
|
|
} else {
|
|
buttonChild = button.update(
|
|
component: ButtonComponent(
|
|
background: buttonBackground,
|
|
content: AnyComponentWithIdentity(
|
|
id: AnyHashable("ok"),
|
|
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Common_OK, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
|
),
|
|
isEnabled: true,
|
|
displaysProgress: false,
|
|
action: { [weak state] in
|
|
guard let state else {
|
|
return
|
|
}
|
|
state.dismiss(animated: true)
|
|
}),
|
|
availableSize: buttonSize,
|
|
transition: context.transition
|
|
)
|
|
}
|
|
|
|
context.add(buttonChild
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + buttonChild.size.height / 2.0))
|
|
)
|
|
originY += buttonChild.size.height
|
|
originY += buttonInsets.bottom
|
|
|
|
// if component.valueInfo.listedCount != nil || component.valueInfo.fragmentListedCount != nil {
|
|
// originY += 5.0
|
|
// }
|
|
//
|
|
// if let listedCount = component.valueInfo.listedCount, let giftIconSubject {
|
|
// let telegramSaleButton = telegramSaleButton.update(
|
|
// component: PlainButtonComponent(
|
|
// content: AnyComponent(
|
|
// HStack([
|
|
// AnyComponentWithIdentity(id: "count", component: AnyComponent(
|
|
// MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
|
|
// Rectangle(color: .clear, width: 8.0, height: 1.0)
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
|
// GiftItemComponent(
|
|
// context: component.context,
|
|
// theme: theme,
|
|
// strings: strings,
|
|
// peer: nil,
|
|
// subject: giftIconSubject,
|
|
// mode: .buttonIcon
|
|
// )
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
|
// MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnTelegram)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
|
// BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
|
// ))
|
|
// ], spacing: 0.0)
|
|
// ),
|
|
// action: { [weak state] in
|
|
// guard let state, let genericGift else {
|
|
// return
|
|
// }
|
|
// state.openGiftResale(gift: genericGift)
|
|
// },
|
|
// animateScale: false
|
|
// ),
|
|
// environment: {},
|
|
// availableSize: context.availableSize,
|
|
// transition: .immediate
|
|
// )
|
|
// context.add(telegramSaleButton
|
|
// .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + telegramSaleButton.size.height / 2.0))
|
|
// )
|
|
// originY += telegramSaleButton.size.height
|
|
// originY += 12.0
|
|
// }
|
|
//
|
|
// if let listedCount = component.valueInfo.fragmentListedCount, let fragmentListedUrl = component.valueInfo.fragmentListedUrl, let giftIconSubject {
|
|
// if component.valueInfo.listedCount != nil {
|
|
// originY += 18.0
|
|
// }
|
|
//
|
|
// let fragmentSaleButton = fragmentSaleButton.update(
|
|
// component: PlainButtonComponent(
|
|
// content: AnyComponent(
|
|
// HStack([
|
|
// AnyComponentWithIdentity(id: "count", component: AnyComponent(
|
|
// MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
|
|
// Rectangle(color: .clear, width: 8.0, height: 1.0)
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
|
// GiftItemComponent(
|
|
// context: component.context,
|
|
// theme: theme,
|
|
// strings: strings,
|
|
// peer: nil,
|
|
// subject: giftIconSubject,
|
|
// mode: .buttonIcon
|
|
// )
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
|
// MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnFragment)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
|
// )),
|
|
// AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
|
// BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
|
// ))
|
|
// ], spacing: 0.0)
|
|
// ),
|
|
// action: { [weak state] in
|
|
// state?.openGiftFragmentResale(url: fragmentListedUrl)
|
|
// },
|
|
// animateScale: false
|
|
// ),
|
|
// environment: {},
|
|
// availableSize: context.availableSize,
|
|
// transition: .immediate
|
|
// )
|
|
// context.add(fragmentSaleButton
|
|
// .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + fragmentSaleButton.size.height / 2.0))
|
|
// )
|
|
// originY += fragmentSaleButton.size.height
|
|
// originY += 12.0
|
|
// }
|
|
|
|
let closeButton = closeButton.update(
|
|
component: GlassBarButtonComponent(
|
|
size: CGSize(width: 40.0, height: 40.0),
|
|
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
|
isDark: theme.overallDarkAppearance,
|
|
state: .generic,
|
|
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
|
BundleIconComponent(
|
|
name: "Navigation/Close",
|
|
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
|
)
|
|
)),
|
|
action: { [weak state] _ in
|
|
guard let state else {
|
|
return
|
|
}
|
|
state.dismiss(animated: true)
|
|
}
|
|
),
|
|
availableSize: CGSize(width: 40.0, height: 40.0),
|
|
transition: .immediate
|
|
)
|
|
context.add(closeButton
|
|
.position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0))
|
|
)
|
|
|
|
let moreButton = moreButton.update(
|
|
component: GlassBarButtonComponent(
|
|
size: CGSize(width: 40.0, height: 40.0),
|
|
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
|
isDark: theme.overallDarkAppearance,
|
|
state: .generic,
|
|
component: AnyComponentWithIdentity(id: "more", component: AnyComponent(
|
|
LottieComponent(
|
|
content: LottieComponent.AppBundleContent(
|
|
name: "anim_morewide"
|
|
),
|
|
color: theme.rootController.navigationBar.glassBarButtonForegroundColor,
|
|
size: CGSize(width: 34.0, height: 34.0),
|
|
playOnce: moreButtonPlayOnce
|
|
)
|
|
)),
|
|
action: { [weak state] view in
|
|
guard let state else {
|
|
return
|
|
}
|
|
state.morePressed(view: view, gesture: nil)
|
|
moreButtonPlayOnce.invoke(Void())
|
|
}
|
|
),
|
|
availableSize: CGSize(width: 40.0, height: 40.0),
|
|
transition: .immediate
|
|
)
|
|
context.add(moreButton
|
|
.position(CGPoint(x: context.availableSize.width - 16.0 - moreButton.size.width / 2.0, y: 16.0 + moreButton.size.height / 2.0))
|
|
)
|
|
|
|
return CGSize(width: context.availableSize.width, height: originY)
|
|
}
|
|
}
|
|
}
|
|
|
|
final class GiftAuctionViewSheetComponent: CombinedComponent {
|
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
|
|
|
let context: AccountContext
|
|
let auctionContext: GiftAuctionContext
|
|
|
|
init(
|
|
context: AccountContext,
|
|
auctionContext: GiftAuctionContext
|
|
) {
|
|
self.context = context
|
|
self.auctionContext = auctionContext
|
|
}
|
|
|
|
static func ==(lhs: GiftAuctionViewSheetComponent, rhs: GiftAuctionViewSheetComponent) -> Bool {
|
|
if lhs.context !== rhs.context {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
static var body: Body {
|
|
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
|
let animateOut = StoredActionSlot(Action<Void>.self)
|
|
|
|
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
|
|
|
return { context in
|
|
let environment = context.environment[EnvironmentType.self]
|
|
let controller = environment.controller
|
|
|
|
let sheet = sheet.update(
|
|
component: SheetComponent<EnvironmentType>(
|
|
content: AnyComponent<EnvironmentType>(GiftAuctionViewSheetContent(
|
|
context: context.component.context,
|
|
auctionContext: context.component.auctionContext,
|
|
animateOut: animateOut,
|
|
getController: controller
|
|
)),
|
|
style: .glass,
|
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
|
followContentSizeChanges: true,
|
|
clipsContent: true,
|
|
autoAnimateOut: false,
|
|
externalState: sheetExternalState,
|
|
animateOut: animateOut,
|
|
onPan: {
|
|
if let controller = controller() as? GiftAuctionViewScreen {
|
|
controller.dismissAllTooltips()
|
|
}
|
|
},
|
|
willDismiss: {
|
|
}
|
|
),
|
|
environment: {
|
|
environment
|
|
SheetComponentEnvironment(
|
|
isDisplaying: environment.value.isVisible,
|
|
isCentered: environment.metrics.widthClass == .regular,
|
|
hasInputHeight: !environment.inputHeight.isZero,
|
|
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
|
dismiss: { animated in
|
|
if animated {
|
|
if let controller = controller() as? GiftAuctionViewScreen {
|
|
controller.dismissAllTooltips()
|
|
animateOut.invoke(Action { _ in
|
|
controller.dismiss(completion: nil)
|
|
})
|
|
}
|
|
} else {
|
|
if let controller = controller() as? GiftAuctionViewScreen {
|
|
controller.dismissAllTooltips()
|
|
controller.dismiss(completion: nil)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
},
|
|
availableSize: context.availableSize,
|
|
transition: context.transition
|
|
)
|
|
|
|
context.add(sheet
|
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
|
)
|
|
|
|
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
|
var sideInset: CGFloat = 0.0
|
|
var bottomInset: CGFloat = max(environment.safeInsets.bottom, sheetExternalState.contentHeight)
|
|
if case .regular = environment.metrics.widthClass {
|
|
sideInset = floor((context.availableSize.width - 430.0) / 2.0) - 12.0
|
|
bottomInset = (context.availableSize.height - sheetExternalState.contentHeight) / 2.0 + sheetExternalState.contentHeight
|
|
}
|
|
|
|
let layout = ContainerViewLayout(
|
|
size: context.availableSize,
|
|
metrics: environment.metrics,
|
|
deviceMetrics: environment.deviceMetrics,
|
|
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0),
|
|
safeInsets: UIEdgeInsets(top: 0.0, left: max(sideInset, environment.safeInsets.left), bottom: 0.0, right: max(sideInset, environment.safeInsets.right)),
|
|
additionalInsets: .zero,
|
|
statusBarHeight: environment.statusBarHeight,
|
|
inputHeight: nil,
|
|
inputHeightIsInteractivellyChanging: false,
|
|
inVoiceOver: false
|
|
)
|
|
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
|
}
|
|
|
|
return context.availableSize
|
|
}
|
|
}
|
|
}
|
|
|
|
public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
|
|
public init(
|
|
context: AccountContext,
|
|
auctionContext: GiftAuctionContext
|
|
) {
|
|
super.init(
|
|
context: context,
|
|
component: GiftAuctionViewSheetComponent(
|
|
context: context,
|
|
auctionContext: auctionContext
|
|
),
|
|
navigationBarAppearance: .none,
|
|
statusBarStyle: .ignore,
|
|
theme: .default
|
|
)
|
|
|
|
self.navigationPresentation = .flatModal
|
|
self.automaticallyControlPresentationContextLayout = false
|
|
}
|
|
|
|
required public init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
}
|
|
|
|
public override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
self.view.disablesInteractiveModalDismiss = true
|
|
}
|
|
|
|
public override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
self.dismissAllTooltips()
|
|
}
|
|
|
|
public func dismissAnimated() {
|
|
self.dismissAllTooltips()
|
|
|
|
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
|
view.dismissAnimated()
|
|
}
|
|
}
|
|
|
|
fileprivate func dismissAllTooltips() {
|
|
self.window?.forEachController({ controller in
|
|
if let controller = controller as? TooltipScreen {
|
|
controller.dismiss(inPlace: false)
|
|
}
|
|
})
|
|
self.forEachController({ controller in
|
|
if let controller = controller as? TooltipScreen {
|
|
controller.dismiss(inPlace: false)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
private final class GiftViewContextReferenceContentSource: 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)
|
|
}
|
|
}
|