mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-01 20:28:05 +00:00
Various improvements
This commit is contained in:
parent
74e1d22de4
commit
5e17b321f8
@ -15167,7 +15167,12 @@ Error: %8$@";
|
|||||||
"ScheduledMessages.Reminder.Delete" = "Delete Reminder";
|
"ScheduledMessages.Reminder.Delete" = "Delete Reminder";
|
||||||
"ScheduledMessages.Reminder.DeleteMany" = "Delete Reminders";
|
"ScheduledMessages.Reminder.DeleteMany" = "Delete Reminders";
|
||||||
|
|
||||||
"Gift.Setup.NextDropIn" = "next drop in {m}:{s}";
|
"Gift.Setup.PlaceBid" = "Place a Bid";
|
||||||
|
"Gift.Setup.AuctionInfo" = "%@ are dropped to the top %@ by bid amount. [Learn more >]()";
|
||||||
|
"Gift.Setup.AuctionInfo.Gifts_1" = "%@ gift";
|
||||||
|
"Gift.Setup.AuctionInfo.Gifts_any" = "%@ gifts";
|
||||||
|
"Gift.Setup.AuctionInfo.Bidders_1" = "%@ bidder";
|
||||||
|
"Gift.Setup.AuctionInfo.Bidders_any" = "%@ bidders";
|
||||||
|
|
||||||
"PrivacySettings.LoginEmailSetupInfo" = "Setup your email address for Telegram login codes.";
|
"PrivacySettings.LoginEmailSetupInfo" = "Setup your email address for Telegram login codes.";
|
||||||
|
|
||||||
@ -15223,6 +15228,11 @@ Error: %8$@";
|
|||||||
"Gift.AuctionBid.Top" = "TOP %@";
|
"Gift.AuctionBid.Top" = "TOP %@";
|
||||||
"Gift.AuctionBid.Custom" = "Custom";
|
"Gift.AuctionBid.Custom" = "Custom";
|
||||||
|
|
||||||
|
"Gift.AuctionBid.CustomBid.Title" = "Place a Custom Bid";
|
||||||
|
"Gift.AuctionBid.CustomBid.Text" = "If you fall below the top %@, your bid will roll over to the next drop.";
|
||||||
|
"Gift.AuctionBid.CustomBid.Placeholder" = "Amount";
|
||||||
|
"Gift.AuctionBid.CustomBid.Done" = "Place a Bid";
|
||||||
|
|
||||||
"Gift.Auction.Context.About" = "About";
|
"Gift.Auction.Context.About" = "About";
|
||||||
"Gift.Auction.Context.CopyLink" = "Copy Link";
|
"Gift.Auction.Context.CopyLink" = "Copy Link";
|
||||||
"Gift.Auction.Context.Share" = "Share";
|
"Gift.Auction.Context.Share" = "Share";
|
||||||
@ -15269,11 +15279,6 @@ Error: %8$@";
|
|||||||
"ChatList.Auctions.Status.Many.OutbidAll" = "You've been outbid in all of them.";
|
"ChatList.Auctions.Status.Many.OutbidAll" = "You've been outbid in all of them.";
|
||||||
"ChatList.Auctions.View" = "View";
|
"ChatList.Auctions.View" = "View";
|
||||||
|
|
||||||
"Gift.Auction.Ongoing.Title" = "One Auction at a Time";
|
|
||||||
"Gift.Auction.Ongoing.Text" = "You’ve already bid in this gift auction for **%@**.";
|
|
||||||
"Gift.Auction.Ongoing.TextYourself" = "You’ve already bid in this gift auction for yourself.";
|
|
||||||
"Gift.Auction.Ongoing.View" = "View";
|
|
||||||
|
|
||||||
"Gift.Auction.Info.Title" = "Auction";
|
"Gift.Auction.Info.Title" = "Auction";
|
||||||
"Gift.Auction.Info.Description" = "Join the battle for exclusive gifts.";
|
"Gift.Auction.Info.Description" = "Join the battle for exclusive gifts.";
|
||||||
|
|
||||||
@ -15368,7 +15373,14 @@ Error: %8$@";
|
|||||||
"Stars.Transaction.LiveStreamPaidMessage_any" = "Fee for %@ Live Stream Messages";
|
"Stars.Transaction.LiveStreamPaidMessage_any" = "Fee for %@ Live Stream Messages";
|
||||||
"Stars.Transaction.LiveStreamPaidMessage.Text" = "You receive **%@%** of the price that you charge for each incoming message.";
|
"Stars.Transaction.LiveStreamPaidMessage.Text" = "You receive **%@%** of the price that you charge for each incoming message.";
|
||||||
|
|
||||||
"Notification.StarGift.Subtitle.NoConvert" = "We'll notify you once it becomes eligible for unique upgrades.";
|
"Notification.StarGift.Subtitle.NoConvert" = "Display this gift on your page and turn it into a collectible.";
|
||||||
"Notification.StarGift.Subtitle.OtherNoConvert" = "We'll notify %1$@ once it becomes eligible for unique upgrades.";
|
"Notification.StarGift.Subtitle.OtherNoConvert" = "Display this gift on your page and turn it into a collectible.";
|
||||||
"Gift.View.NoConvertDescription" = "We'll notify you once it becomes eligible for unique upgrades.";
|
"Gift.View.NoConvertDescription" = "We will notify you once it becomes eligible for unique upgrades.";
|
||||||
"Gift.View.OtherNoConvertDescription" = "We'll notify %1$@ once it becomes eligible for unique upgrades.";
|
"Gift.View.OtherNoConvertDescription" = "We will notify %1$@ once it becomes eligible for unique upgrades.";
|
||||||
|
|
||||||
|
"Gift.AuctionTransfer.Title" = "Change Recipient";
|
||||||
|
"Gift.AuctionTransfer.Text" = "The current recipient of this gift is **%@**. Change to **%@**?";
|
||||||
|
"Gift.AuctionTransfer.TextFromYourself" = "The current recipient of this gift is you. Change to **%@**?";
|
||||||
|
"Gift.AuctionTransfer.TextToYourself" = "The current recipient of this gift is **%@**. Change to yourself?";
|
||||||
|
"Gift.AuctionTransfer.Change" = "Change";
|
||||||
|
|
||||||
|
|||||||
@ -1423,8 +1423,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController
|
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController
|
||||||
func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
||||||
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
|
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
|
||||||
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController
|
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext) -> ViewController
|
||||||
func makeGiftAuctionViewScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController
|
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping () -> Void) -> ViewController
|
||||||
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
|
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
|
||||||
|
|
||||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||||
|
|||||||
@ -565,7 +565,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
guard let self, let auction else {
|
guard let self, let auction else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: auction.currentBidPeerId ?? self.context.account.peerId, auctionContext: auction)
|
let controller = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: auction.currentBidPeerId ?? self.context.account.peerId, text: nil, entities: nil, hideName: false, auctionContext: auction)
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public enum BotPaymentInvoiceSource {
|
|||||||
case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool)
|
case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool)
|
||||||
case starGiftPrepaidUpgrade(peerId: EnginePeer.Id, hash: String)
|
case starGiftPrepaidUpgrade(peerId: EnginePeer.Id, hash: String)
|
||||||
case starGiftDropOriginalDetails(reference: StarGiftReference)
|
case starGiftDropOriginalDetails(reference: StarGiftReference)
|
||||||
case starGiftAuctionBid(update: Bool, hideName: Bool, peerId: EnginePeer.Id, giftId: Int64, bidAmount: Int64, text: String?, entities: [MessageTextEntity]?)
|
case starGiftAuctionBid(update: Bool, hideName: Bool, peerId: EnginePeer.Id?, giftId: Int64, bidAmount: Int64, text: String?, entities: [MessageTextEntity]?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct BotPaymentInvoiceFields: OptionSet {
|
public struct BotPaymentInvoiceFields: OptionSet {
|
||||||
@ -426,23 +426,28 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv
|
|||||||
return reference.apiStarGiftReference(transaction: transaction).flatMap { .inputInvoiceStarGiftDropOriginalDetails(stargift: $0) }
|
return reference.apiStarGiftReference(transaction: transaction).flatMap { .inputInvoiceStarGiftDropOriginalDetails(stargift: $0) }
|
||||||
|
|
||||||
case let .starGiftAuctionBid(update, hideName, peerId, giftId, bidAmount, text, entities):
|
case let .starGiftAuctionBid(update, hideName, peerId, giftId, bidAmount, text, entities):
|
||||||
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if hideName {
|
var inputPeer: Api.InputPeer?
|
||||||
flags |= (1 << 0)
|
var message: Api.TextWithEntities?
|
||||||
}
|
|
||||||
if update {
|
if update {
|
||||||
flags |= (1 << 2)
|
flags |= (1 << 2)
|
||||||
}
|
}
|
||||||
|
if let peerId {
|
||||||
|
guard let peer = transaction.getPeer(peerId).flatMap(apiInputPeer) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
flags |= (1 << 3)
|
flags |= (1 << 3)
|
||||||
|
inputPeer = peer
|
||||||
|
|
||||||
|
if hideName {
|
||||||
|
flags |= (1 << 0)
|
||||||
|
}
|
||||||
|
|
||||||
var message: Api.TextWithEntities?
|
|
||||||
if let text, !text.isEmpty {
|
if let text, !text.isEmpty {
|
||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? [])
|
message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? [])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return .inputInvoiceStarGiftAuctionBid(flags: flags, peer: inputPeer, giftId: giftId, bidAmount: bidAmount, message: message)
|
return .inputInvoiceStarGiftAuctionBid(flags: flags, peer: inputPeer, giftId: giftId, bidAmount: bidAmount, message: message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -464,6 +464,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
|
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
|
||||||
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
|
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
|
||||||
"//submodules/TelegramUI/Components/Gifts/GiftStoreScreen",
|
"//submodules/TelegramUI/Components/Gifts/GiftStoreScreen",
|
||||||
|
"//submodules/TelegramUI/Components/Gifts/GiftSetupScreen",
|
||||||
"//submodules/TelegramUI/Components/ContentReportScreen",
|
"//submodules/TelegramUI/Components/ContentReportScreen",
|
||||||
"//submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen",
|
"//submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen",
|
||||||
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
||||||
|
|||||||
@ -556,7 +556,11 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, _, channelPeerId, senderPeerId, _, _, _, _, _, toPeerId):
|
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, _, channelPeerId, senderPeerId, _, _, _, _, _, toPeerId):
|
||||||
var incoming = incoming
|
var incoming = incoming
|
||||||
|
var convertStars = convertStars
|
||||||
if case let .generic(gift) = gift {
|
if case let .generic(gift) = gift {
|
||||||
|
if gift.flags.contains(.isAuction) {
|
||||||
|
convertStars = nil
|
||||||
|
}
|
||||||
if let releasedBy = gift.releasedBy, let peer = item.message.peers[releasedBy], let addressName = peer.addressName {
|
if let releasedBy = gift.releasedBy, let peer = item.message.peers[releasedBy], let addressName = peer.addressName {
|
||||||
creatorButtonTitle = item.presentationData.strings.Notification_StarGift_ReleasedBy("**@\(addressName)**").string
|
creatorButtonTitle = item.presentationData.strings.Notification_StarGift_ReleasedBy("**@\(addressName)**").string
|
||||||
}
|
}
|
||||||
@ -656,7 +660,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold))
|
entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
text = item.presentationData.strings.Notification_StarGift_Subtitle_OtherNoConvert(peerName).string
|
text = item.presentationData.strings.Notification_StarGift_Subtitle_OtherNoConvert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -441,7 +441,7 @@ public final class GiftItemComponent: Component {
|
|||||||
let _ = loadingBackground.update(
|
let _ = loadingBackground.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
ItemShimmeringLoadingComponent(color: component.theme.list.itemAccentColor, cornerRadius: 10.0)
|
ItemShimmeringLoadingComponent(color: component.theme.list.itemAccentColor, cornerRadius: cornerRadius)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: size
|
containerSize: size
|
||||||
|
|||||||
@ -0,0 +1,267 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import AccountContext
|
||||||
|
import AppBundle
|
||||||
|
import AvatarNode
|
||||||
|
import Markdown
|
||||||
|
|
||||||
|
private final class GiftAuctionTransferAlertContentNode: AlertContentNode {
|
||||||
|
private let strings: PresentationStrings
|
||||||
|
private let title: String
|
||||||
|
private let text: String
|
||||||
|
|
||||||
|
private let titleNode: ASTextNode
|
||||||
|
private let textNode: ASTextNode
|
||||||
|
private let avatarNode: AvatarNode
|
||||||
|
private let arrowNode: ASImageNode
|
||||||
|
private let secondAvatarNode: AvatarNode
|
||||||
|
|
||||||
|
private let actionNodesSeparator: ASDisplayNode
|
||||||
|
private let actionNodes: [TextAlertContentActionNode]
|
||||||
|
private let actionVerticalSeparators: [ASDisplayNode]
|
||||||
|
|
||||||
|
private var validLayout: CGSize?
|
||||||
|
|
||||||
|
override var dismissOnOutsideTap: Bool {
|
||||||
|
return self.isUserInteractionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, fromPeer: EnginePeer, toPeer: EnginePeer, title: String, text: String, actions: [TextAlertAction]) {
|
||||||
|
self.strings = strings
|
||||||
|
self.title = title
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
self.titleNode = ASTextNode()
|
||||||
|
self.titleNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
|
self.textNode = ASTextNode()
|
||||||
|
self.textNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
|
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||||
|
|
||||||
|
self.arrowNode = ASImageNode()
|
||||||
|
self.arrowNode.displaysAsynchronously = false
|
||||||
|
self.arrowNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.secondAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||||
|
|
||||||
|
self.actionNodesSeparator = ASDisplayNode()
|
||||||
|
self.actionNodesSeparator.isLayerBacked = true
|
||||||
|
|
||||||
|
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||||
|
return TextAlertContentActionNode(theme: theme, action: action)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||||
|
if actions.count > 1 {
|
||||||
|
for _ in 0 ..< actions.count - 1 {
|
||||||
|
let separatorNode = ASDisplayNode()
|
||||||
|
separatorNode.isLayerBacked = true
|
||||||
|
actionVerticalSeparators.append(separatorNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.actionVerticalSeparators = actionVerticalSeparators
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.titleNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
self.addSubnode(self.avatarNode)
|
||||||
|
self.addSubnode(self.arrowNode)
|
||||||
|
self.addSubnode(self.secondAvatarNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.actionNodesSeparator)
|
||||||
|
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
self.addSubnode(actionNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
for separatorNode in self.actionVerticalSeparators {
|
||||||
|
self.addSubnode(separatorNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateTheme(theme)
|
||||||
|
|
||||||
|
self.avatarNode.setPeer(context: context, theme: ptheme, peer: fromPeer)
|
||||||
|
self.secondAvatarNode.setPeer(context: context, theme: ptheme, peer: toPeer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||||
|
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
), textAlignment: .center)
|
||||||
|
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor)
|
||||||
|
|
||||||
|
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
actionNode.updateTheme(theme)
|
||||||
|
}
|
||||||
|
for separatorNode in self.actionVerticalSeparators {
|
||||||
|
separatorNode.backgroundColor = theme.separatorColor
|
||||||
|
}
|
||||||
|
|
||||||
|
if let size = self.validLayout {
|
||||||
|
_ = self.updateLayout(size: size, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
|
var size = size
|
||||||
|
size.width = min(size.width, 270.0)
|
||||||
|
|
||||||
|
self.validLayout = size
|
||||||
|
|
||||||
|
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||||
|
|
||||||
|
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||||
|
self.avatarNode.updateSize(size: avatarSize)
|
||||||
|
|
||||||
|
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||||
|
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||||
|
|
||||||
|
if let arrowImage = self.arrowNode.image {
|
||||||
|
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||||
|
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
let secondAvatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize)
|
||||||
|
transition.updateFrame(node: self.secondAvatarNode, frame: secondAvatarFrame)
|
||||||
|
|
||||||
|
origin.y += avatarSize.height + 10.0
|
||||||
|
|
||||||
|
let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||||
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||||
|
origin.y += titleSize.height + 4.0
|
||||||
|
|
||||||
|
let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||||
|
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||||
|
origin.y += textSize.height + 10.0
|
||||||
|
|
||||||
|
let actionButtonHeight: CGFloat = 44.0
|
||||||
|
var minActionsWidth: CGFloat = 0.0
|
||||||
|
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||||
|
let actionTitleInsets: CGFloat = 8.0
|
||||||
|
|
||||||
|
var effectiveActionLayout = TextAlertContentActionLayout.horizontal
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||||
|
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||||
|
effectiveActionLayout = .vertical
|
||||||
|
}
|
||||||
|
switch effectiveActionLayout {
|
||||||
|
case .horizontal:
|
||||||
|
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||||
|
case .vertical:
|
||||||
|
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||||
|
|
||||||
|
let contentWidth = max(size.width, minActionsWidth)
|
||||||
|
|
||||||
|
var actionsHeight: CGFloat = 0.0
|
||||||
|
switch effectiveActionLayout {
|
||||||
|
case .horizontal:
|
||||||
|
actionsHeight = actionButtonHeight
|
||||||
|
case .vertical:
|
||||||
|
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom)
|
||||||
|
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
|
var actionOffset: CGFloat = 0.0
|
||||||
|
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||||
|
var separatorIndex = -1
|
||||||
|
var nodeIndex = 0
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
if separatorIndex >= 0 {
|
||||||
|
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||||
|
switch effectiveActionLayout {
|
||||||
|
case .horizontal:
|
||||||
|
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||||
|
case .vertical:
|
||||||
|
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
separatorIndex += 1
|
||||||
|
|
||||||
|
let currentActionWidth: CGFloat
|
||||||
|
switch effectiveActionLayout {
|
||||||
|
case .horizontal:
|
||||||
|
if nodeIndex == self.actionNodes.count - 1 {
|
||||||
|
currentActionWidth = resultSize.width - actionOffset
|
||||||
|
} else {
|
||||||
|
currentActionWidth = actionWidth
|
||||||
|
}
|
||||||
|
case .vertical:
|
||||||
|
currentActionWidth = resultSize.width
|
||||||
|
}
|
||||||
|
|
||||||
|
let actionNodeFrame: CGRect
|
||||||
|
switch effectiveActionLayout {
|
||||||
|
case .horizontal:
|
||||||
|
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||||
|
actionOffset += currentActionWidth
|
||||||
|
case .vertical:
|
||||||
|
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||||
|
actionOffset += actionButtonHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||||
|
|
||||||
|
nodeIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer, toPeer: EnginePeer, commit: @escaping () -> Void) -> AlertController {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let strings = presentationData.strings
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
if fromPeer.id == context.account.peerId {
|
||||||
|
text = strings.Gift_AuctionTransfer_TextFromYourself(toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||||
|
} else if toPeer.id == context.account.peerId {
|
||||||
|
text = strings.Gift_AuctionTransfer_TextToYourself(fromPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||||
|
} else {
|
||||||
|
text = strings.Gift_AuctionTransfer_Text(fromPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||||
|
}
|
||||||
|
|
||||||
|
var dismissImpl: ((Bool) -> Void)?
|
||||||
|
var contentNode: GiftAuctionTransferAlertContentNode?
|
||||||
|
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
dismissImpl?(true)
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: strings.Gift_AuctionTransfer_Change, action: {
|
||||||
|
dismissImpl?(true)
|
||||||
|
commit()
|
||||||
|
})]
|
||||||
|
|
||||||
|
contentNode = GiftAuctionTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, fromPeer: fromPeer, toPeer: toPeer, title: strings.Gift_AuctionTransfer_Title, text: text, actions: actions)
|
||||||
|
|
||||||
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!)
|
||||||
|
dismissImpl = { [weak controller] animated in
|
||||||
|
if animated {
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
} else {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
@ -390,44 +390,47 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
let giftController = component.context.sharedContext.makeGiftAuctionBidScreen(
|
let giftController = component.context.sharedContext.makeGiftAuctionBidScreen(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
toPeerId: currentBidPeerId,
|
toPeerId: currentBidPeerId,
|
||||||
|
text: nil,
|
||||||
|
entities: nil,
|
||||||
|
hideName: false,
|
||||||
auctionContext: auctionContext
|
auctionContext: auctionContext
|
||||||
)
|
)
|
||||||
mainController.push(giftController)
|
mainController.push(giftController)
|
||||||
} else {
|
} else {
|
||||||
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: currentBidPeerId))
|
let _ = (context.engine.data.get(
|
||||||
|> deliverOnMainQueue).start(next: { [weak self, weak mainController] peer in
|
TelegramEngine.EngineData.Item.Peer.Peer(id: currentBidPeerId),
|
||||||
guard let component = self?.component, let environment = self?.environment, let mainController else {
|
TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId)
|
||||||
return
|
|
||||||
}
|
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let alertController = textAlertController(
|
|
||||||
context: component.context,
|
|
||||||
title: environment.strings.Gift_Auction_Ongoing_Title,
|
|
||||||
text: auctionContext.currentBidPeerId == component.context.account.peerId ? environment.strings.Gift_Auction_Ongoing_TextYourself : environment.strings.Gift_Auction_Ongoing_Text(peer?.displayTitle(strings: environment.strings, displayOrder: presentationData.nameDisplayOrder) ?? "").string,
|
|
||||||
actions: [
|
|
||||||
TextAlertAction(type: .genericAction, title: environment.strings.Common_OK, action: {}),
|
|
||||||
TextAlertAction(type: .defaultAction, title: environment.strings.Gift_Auction_Ongoing_View, action: { [weak mainController] in
|
|
||||||
guard let mainController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let giftController = component.context.sharedContext.makeGiftAuctionBidScreen(
|
|
||||||
context: component.context,
|
|
||||||
toPeerId: currentBidPeerId,
|
|
||||||
auctionContext: auctionContext
|
|
||||||
)
|
)
|
||||||
mainController.push(giftController)
|
|> deliverOnMainQueue).start(next: { [weak self, weak mainController] fromPeer, toPeer in
|
||||||
|
guard let component = self?.component, let mainController, let fromPeer, let toPeer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let alertController = giftAuctionTransferController(context: context, fromPeer: fromPeer, toPeer: toPeer, commit: {
|
||||||
|
let controller = GiftSetupScreen(
|
||||||
|
context: context,
|
||||||
|
peerId: component.peerId,
|
||||||
|
subject: .starGift(gift, nil),
|
||||||
|
completion: nil
|
||||||
|
)
|
||||||
|
mainController.push(controller)
|
||||||
})
|
})
|
||||||
],
|
|
||||||
parseMarkdown: true
|
|
||||||
)
|
|
||||||
mainController.present(alertController, in: .window(.root))
|
mainController.present(alertController, in: .window(.root))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let giftController = component.context.sharedContext.makeGiftAuctionViewScreen(
|
let giftController = component.context.sharedContext.makeGiftAuctionViewScreen(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
toPeerId: component.peerId,
|
auctionContext: auctionContext,
|
||||||
auctionContext: auctionContext
|
completion: { [weak mainController] in
|
||||||
|
let controller = GiftSetupScreen(
|
||||||
|
context: context,
|
||||||
|
peerId: component.peerId,
|
||||||
|
subject: .starGift(gift, nil),
|
||||||
|
completion: nil
|
||||||
|
)
|
||||||
|
mainController?.push(controller)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
mainController.push(giftController)
|
mainController.push(giftController)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,6 +146,11 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
private var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
private var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||||
private var sendPaidMessageStars: StarsAmount?
|
private var sendPaidMessageStars: StarsAmount?
|
||||||
|
|
||||||
|
private var giftAuction: GiftAuctionContext?
|
||||||
|
private var giftAuctionState: GiftAuctionContext.State?
|
||||||
|
private var giftAuctionDisposable: Disposable?
|
||||||
|
private var giftAuctionTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
private var cachedStarImage: (UIImage, PresentationTheme)?
|
private var cachedStarImage: (UIImage, PresentationTheme)?
|
||||||
|
|
||||||
private var updateDisposable: Disposable?
|
private var updateDisposable: Disposable?
|
||||||
@ -227,6 +232,8 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
self.inputMediaNodeDataDisposable?.dispose()
|
self.inputMediaNodeDataDisposable?.dispose()
|
||||||
self.updateDisposable?.dispose()
|
self.updateDisposable?.dispose()
|
||||||
self.optionsDisposable?.dispose()
|
self.optionsDisposable?.dispose()
|
||||||
|
self.giftAuctionDisposable?.dispose()
|
||||||
|
self.giftAuctionTimer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
@ -452,12 +459,11 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func proceedWithStarGift() {
|
private func proceedWithStarGift() {
|
||||||
guard let component = self.component, let starsContext = component.context.starsContext, let starsState = starsContext.currentState else {
|
guard let component = self.component, let environment = self.environment, let starsContext = component.context.starsContext, let starsState = starsContext.currentState else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let context = component.context
|
let context = component.context
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let peerId = component.peerId
|
let peerId = component.peerId
|
||||||
|
|
||||||
var textInputText = NSAttributedString()
|
var textInputText = NSAttributedString()
|
||||||
@ -466,6 +472,22 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
let entities = generateChatInputTextEntities(textInputText)
|
let entities = generateChatInputTextEntities(textInputText)
|
||||||
|
|
||||||
|
if case let .starGift(gift, _) = component.subject, gift.flags.contains(.isAuction), let navigationController = environment.controller()?.navigationController as? NavigationController, let auctionContext = self.giftAuction {
|
||||||
|
let controller = context.sharedContext.makeGiftAuctionBidScreen(
|
||||||
|
context: context,
|
||||||
|
toPeerId: peerId,
|
||||||
|
text: textInputText.string,
|
||||||
|
entities: entities,
|
||||||
|
hideName: self.hideName,
|
||||||
|
auctionContext: auctionContext
|
||||||
|
)
|
||||||
|
environment.controller()?.dismiss()
|
||||||
|
navigationController.pushViewController(controller)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
var finalPrice: Int64
|
var finalPrice: Int64
|
||||||
var perUserLimit: Int32?
|
var perUserLimit: Int32?
|
||||||
var giftFile: TelegramMediaFile?
|
var giftFile: TelegramMediaFile?
|
||||||
@ -880,6 +902,29 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
self.hideName = true
|
self.hideName = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .starGift(gift, _) = component.subject, gift.flags.contains(.isAuction), let giftAuctionsManager = component.context.giftAuctionsManager {
|
||||||
|
let _ = (giftAuctionsManager.auctionContext(for: .giftId(gift.id))
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] auctionContext in
|
||||||
|
guard let self, let auctionContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.giftAuction = auctionContext
|
||||||
|
self.giftAuctionDisposable = (auctionContext.state
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.giftAuctionState = state
|
||||||
|
self.state?.updated()
|
||||||
|
})
|
||||||
|
|
||||||
|
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||||
|
self?.state?.updated()
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
self.giftAuctionTimer?.start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var releasedBy: EnginePeer.Id?
|
var releasedBy: EnginePeer.Id?
|
||||||
if case let .starGift(gift, true) = component.subject, gift.upgradeStars != nil {
|
if case let .starGift(gift, true) = component.subject, gift.upgradeStars != nil {
|
||||||
self.includeUpgrade = true
|
self.includeUpgrade = true
|
||||||
@ -1727,9 +1772,54 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
contentHeight += remainingCountSize.height
|
contentHeight += remainingCountSize.height
|
||||||
contentHeight += 7.0
|
contentHeight += 7.0
|
||||||
|
|
||||||
contentHeight += sectionSpacing
|
if starGift.flags.contains(.isAuction), let giftsPerRound = starGift.auctionGiftsPerRound {
|
||||||
|
let parsedString = parseMarkdownIntoAttributedString(environment.strings.Gift_Setup_AuctionInfo(environment.strings.Gift_Setup_AuctionInfo_Gifts(giftsPerRound), environment.strings.Gift_Setup_AuctionInfo_Bidders(giftsPerRound)).string, attributes: footerAttributes)
|
||||||
|
let auctionFooterText = NSMutableAttributedString(attributedString: parsedString)
|
||||||
|
|
||||||
|
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme {
|
||||||
|
self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
|
||||||
|
}
|
||||||
|
if let range = auctionFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
||||||
|
auctionFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: auctionFooterText.string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let auctionFooterSize = self.auctionFooter.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(auctionFooterText),
|
||||||
|
maximumNumberOfLines: 0,
|
||||||
|
highlightColor: environment.theme.list.itemAccentColor.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: { [weak self] _, _ in
|
||||||
|
guard let self, let component = self.component, let controller = self.environment?.controller(), let auctionContext = self.giftAuction else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let infoController = component.context.sharedContext.makeGiftAuctionInfoScreen(context: component.context, auctionContext: auctionContext, completion: nil)
|
||||||
|
controller.push(infoController)
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 16.0 * 2.0, height: 10000.0)
|
||||||
|
)
|
||||||
|
let auctionFooterFrame = CGRect(origin: CGPoint(x: sideInset + 16.0, y: contentHeight), size: auctionFooterSize)
|
||||||
|
if let auctionFooterView = self.auctionFooter.view {
|
||||||
|
if auctionFooterView.superview == nil {
|
||||||
|
self.scrollContentView.addSubview(auctionFooterView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: auctionFooterView, frame: auctionFooterFrame)
|
||||||
|
}
|
||||||
|
contentHeight += auctionFooterSize.height
|
||||||
|
}
|
||||||
|
|
||||||
|
contentHeight += sectionSpacing
|
||||||
|
}
|
||||||
|
|
||||||
initialContentHeight = contentHeight
|
initialContentHeight = contentHeight
|
||||||
|
|
||||||
@ -1737,7 +1827,6 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
|
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var buttonIsEnabled = true
|
var buttonIsEnabled = true
|
||||||
let buttonString: String
|
let buttonString: String
|
||||||
switch component.subject {
|
switch component.subject {
|
||||||
@ -1763,7 +1852,63 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
|
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
|
||||||
|
if let _ = self.giftAuction {
|
||||||
|
let buttonAttributedString = NSMutableAttributedString(string: environment.strings.Gift_Setup_PlaceBid, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||||
|
buttonTitleItems.append(AnyComponentWithIdentity(id: "bid", component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(buttonAttributedString))
|
||||||
|
)))
|
||||||
|
if let giftAuctionState = self.giftAuctionState {
|
||||||
|
switch giftAuctionState.auctionState {
|
||||||
|
case let .ongoing(_, _, endTime, _, _, _, _, _, _, _):
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
|
||||||
|
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 ? environment.strings.Gift_Auction_TimeLeftHours : environment.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]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonTitleItems.append(AnyComponentWithIdentity(id: "timer", component: AnyComponent(AnimatedTextComponent(
|
||||||
|
font: Font.with(size: 12.0, weight: .medium, traits: .monospacedNumbers),
|
||||||
|
color: environment.theme.list.itemCheckColors.foregroundColor.withAlphaComponent(0.7),
|
||||||
|
items: buttonAnimatedTitleItems,
|
||||||
|
noDelay: true
|
||||||
|
))))
|
||||||
|
case .finished:
|
||||||
|
buttonIsEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||||
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
|
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
|
||||||
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
|
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
|
||||||
@ -1775,6 +1920,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||||||
buttonTitleItems.append(AnyComponentWithIdentity(id: buttonString, component: AnyComponent(
|
buttonTitleItems.append(AnyComponentWithIdentity(id: buttonString, component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(buttonAttributedString))
|
MultilineTextComponent(text: .plain(buttonAttributedString))
|
||||||
)))
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 32.0)
|
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 32.0)
|
||||||
let buttonHeight: CGFloat = 52.0
|
let buttonHeight: CGFloat = 52.0
|
||||||
|
|||||||
@ -49,6 +49,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
||||||
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
|
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
|
||||||
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
||||||
|
"//submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen",
|
||||||
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
||||||
"//submodules/ActivityIndicator",
|
"//submodules/ActivityIndicator",
|
||||||
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
||||||
|
|||||||
@ -332,7 +332,7 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
|
|
||||||
let bidController = component.context.sharedContext.makeGiftAuctionBidScreen(context: component.context, toPeerId: auction.currentBidPeerId ?? component.context.account.peerId, auctionContext: auction)
|
let bidController = component.context.sharedContext.makeGiftAuctionBidScreen(context: component.context, toPeerId: auction.currentBidPeerId ?? component.context.account.peerId, text: nil, entities: nil, hideName: false, auctionContext: auction)
|
||||||
navigationController.pushViewController(bidController)
|
navigationController.pushViewController(bidController)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -940,17 +940,26 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let toPeerId: EnginePeer.Id
|
let toPeerId: EnginePeer.Id
|
||||||
|
let text: String?
|
||||||
|
let entities: [MessageTextEntity]?
|
||||||
|
let hideName: Bool
|
||||||
let gift: StarGift
|
let gift: StarGift
|
||||||
let auctionContext: GiftAuctionContext
|
let auctionContext: GiftAuctionContext
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
toPeerId: EnginePeer.Id,
|
toPeerId: EnginePeer.Id,
|
||||||
|
text: String?,
|
||||||
|
entities: [MessageTextEntity]?,
|
||||||
|
hideName: Bool,
|
||||||
gift: StarGift,
|
gift: StarGift,
|
||||||
auctionContext: GiftAuctionContext
|
auctionContext: GiftAuctionContext
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.toPeerId = toPeerId
|
self.toPeerId = toPeerId
|
||||||
|
self.text = text
|
||||||
|
self.entities = entities
|
||||||
|
self.hideName = hideName
|
||||||
self.gift = gift
|
self.gift = gift
|
||||||
self.auctionContext = auctionContext
|
self.auctionContext = auctionContext
|
||||||
}
|
}
|
||||||
@ -994,7 +1003,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
|
|
||||||
private static func makeSliderSteps(minRealValue: Int, maxRealValue: Int, isLogarithmic: Bool) -> [Int] {
|
private static func makeSliderSteps(minRealValue: Int, maxRealValue: Int, isLogarithmic: Bool) -> [Int] {
|
||||||
if isLogarithmic {
|
if isLogarithmic {
|
||||||
var sliderSteps: [Int] = [1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 15_000, 20_000, 30_000, 40_000, 50_000]
|
var sliderSteps: [Int] = [1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 30_000, 40_000, 50_000]
|
||||||
sliderSteps.removeAll(where: { $0 <= minRealValue })
|
sliderSteps.removeAll(where: { $0 <= minRealValue })
|
||||||
sliderSteps.insert(minRealValue, at: 0)
|
sliderSteps.insert(minRealValue, at: 0)
|
||||||
sliderSteps.removeAll(where: { $0 >= maxRealValue })
|
sliderSteps.removeAll(where: { $0 >= maxRealValue })
|
||||||
@ -1423,6 +1432,10 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isUpdate = false
|
var isUpdate = false
|
||||||
|
var myBidPeerId: EnginePeer.Id?
|
||||||
|
if let peerId = self.giftAuctionState?.myState.bidPeerId {
|
||||||
|
myBidPeerId = peerId
|
||||||
|
}
|
||||||
if let myBidAmount = self.giftAuctionState?.myState.bidAmount {
|
if let myBidAmount = self.giftAuctionState?.myState.bidAmount {
|
||||||
isUpdate = true
|
isUpdate = true
|
||||||
if value == myBidAmount {
|
if value == myBidAmount {
|
||||||
@ -1456,14 +1469,19 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
self.state?.updated()
|
self.state?.updated()
|
||||||
|
|
||||||
|
var peerId: EnginePeer.Id?
|
||||||
|
if !isUpdate || (myBidPeerId != nil && myBidPeerId != component.toPeerId) {
|
||||||
|
peerId = component.toPeerId
|
||||||
|
}
|
||||||
|
|
||||||
let source: BotPaymentInvoiceSource = .starGiftAuctionBid(
|
let source: BotPaymentInvoiceSource = .starGiftAuctionBid(
|
||||||
update: isUpdate,
|
update: isUpdate,
|
||||||
hideName: false,
|
hideName: peerId != nil ? component.hideName : false,
|
||||||
peerId: component.toPeerId,
|
peerId: peerId,
|
||||||
giftId: gift.id,
|
giftId: gift.id,
|
||||||
bidAmount: value,
|
bidAmount: value,
|
||||||
text: nil,
|
text: peerId != nil ? component.text : nil,
|
||||||
entities: nil
|
entities: peerId != nil ? component.entities : nil
|
||||||
)
|
)
|
||||||
|
|
||||||
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|
||||||
@ -1489,7 +1507,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
|
|
||||||
let newMaxValue = Int(Double(value) * 1.5)
|
let newMaxValue = Int(Double(value) * 1.5)
|
||||||
var updatedAmount = self.amount.withMinAllowedRealValue(Int(value))
|
var updatedAmount = self.amount.withMinAllowedRealValue(Int(value)).withRealValue(Int(value))
|
||||||
if newMaxValue > self.amount.maxRealValue {
|
if newMaxValue > self.amount.maxRealValue {
|
||||||
updatedAmount = updatedAmount.withMaxRealValue(newMaxValue)
|
updatedAmount = updatedAmount.withMaxRealValue(newMaxValue)
|
||||||
}
|
}
|
||||||
@ -1682,15 +1700,33 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func presentCustomBidController() {
|
func presentCustomBidController() {
|
||||||
guard let component = self.component else {
|
guard let component = self.component, let environment = self.environment, case let .generic(gift) = component.gift else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let auctionState = self.giftAuctionState else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var minBidAmount: Int64 = 100
|
||||||
|
if case let .ongoing(_, _, _, auctionMinBidAmount, _, _, _, _, _, _) = auctionState.auctionState {
|
||||||
|
minBidAmount = auctionMinBidAmount
|
||||||
|
if let myMinBidAmount = auctionState.myState.minBidAmount {
|
||||||
|
minBidAmount = myMinBidAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let giftsPerRounds = gift.auctionGiftsPerRound ?? 50
|
||||||
|
|
||||||
let controller = giftAuctionCustomBidController(
|
let controller = giftAuctionCustomBidController(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
title: "Place a Custom Bid",
|
title: environment.strings.Gift_AuctionBid_CustomBid_Title,
|
||||||
text: "Description",
|
text: environment.strings.Gift_AuctionBid_CustomBid_Text("\(giftsPerRounds)").string,
|
||||||
placeholder: "Bid",
|
placeholder: environment.strings.Gift_AuctionBid_CustomBid_Placeholder,
|
||||||
value: 100,
|
action: environment.strings.Gift_AuctionBid_CustomBid_Done,
|
||||||
|
minValue: minBidAmount,
|
||||||
|
value: minBidAmount,
|
||||||
apply: { [weak self] value in
|
apply: { [weak self] value in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -2698,12 +2734,15 @@ public class GiftAuctionBidScreen: ViewControllerComponentContainer {
|
|||||||
private var didPlayAppearAnimation: Bool = false
|
private var didPlayAppearAnimation: Bool = false
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
public init(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) {
|
public init(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
super.init(context: context, component: GiftAuctionBidScreenComponent(
|
super.init(context: context, component: GiftAuctionBidScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
toPeerId: toPeerId,
|
toPeerId: toPeerId,
|
||||||
|
text: text,
|
||||||
|
entities: entities,
|
||||||
|
hideName: hideName,
|
||||||
gift: auctionContext.gift,
|
gift: auctionContext.gift,
|
||||||
auctionContext: auctionContext
|
auctionContext: auctionContext
|
||||||
), navigationBarAppearance: .none, theme: .default)
|
), navigationBarAppearance: .none, theme: .default)
|
||||||
|
|||||||
@ -7,168 +7,23 @@ import TelegramCore
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import UrlEscaping
|
import UrlEscaping
|
||||||
|
import ComponentFlow
|
||||||
|
import StarsWithdrawalScreen
|
||||||
|
|
||||||
private final class GiftAuctionCustomBidInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
|
private final class GiftAuctionCustomBidAlertContentNode: AlertContentNode {
|
||||||
private var theme: PresentationTheme
|
private let theme: PresentationTheme
|
||||||
private let backgroundNode: ASImageNode
|
|
||||||
private let textInputNode: EditableTextNode
|
|
||||||
private let placeholderNode: ASTextNode
|
|
||||||
|
|
||||||
var updateHeight: (() -> Void)?
|
|
||||||
var complete: (() -> Void)?
|
|
||||||
var textChanged: ((String) -> Void)?
|
|
||||||
|
|
||||||
private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0)
|
|
||||||
private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
|
|
||||||
|
|
||||||
var text: String {
|
|
||||||
get {
|
|
||||||
return self.textInputNode.attributedText?.string ?? ""
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor)
|
|
||||||
self.placeholderNode.isHidden = !newValue.isEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var placeholder: String = "" {
|
|
||||||
didSet {
|
|
||||||
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(theme: PresentationTheme, placeholder: String, returnKeyType: UIReturnKeyType = .done) {
|
|
||||||
self.theme = theme
|
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
|
||||||
self.backgroundNode.isLayerBacked = true
|
|
||||||
self.backgroundNode.displaysAsynchronously = false
|
|
||||||
self.backgroundNode.displayWithoutProcessing = true
|
|
||||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
|
||||||
|
|
||||||
self.textInputNode = EditableTextNode()
|
|
||||||
self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor]
|
|
||||||
self.textInputNode.clipsToBounds = true
|
|
||||||
self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
|
|
||||||
self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0)
|
|
||||||
self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
|
|
||||||
self.textInputNode.keyboardType = .default
|
|
||||||
self.textInputNode.autocapitalizationType = .sentences
|
|
||||||
self.textInputNode.returnKeyType = returnKeyType
|
|
||||||
self.textInputNode.autocorrectionType = .default
|
|
||||||
self.textInputNode.tintColor = theme.actionSheet.controlAccentColor
|
|
||||||
self.textInputNode.keyboardType = .numberPad
|
|
||||||
|
|
||||||
self.placeholderNode = ASTextNode()
|
|
||||||
self.placeholderNode.isUserInteractionEnabled = false
|
|
||||||
self.placeholderNode.displaysAsynchronously = false
|
|
||||||
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.textInputNode.delegate = self
|
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
|
||||||
self.addSubnode(self.textInputNode)
|
|
||||||
self.addSubnode(self.placeholderNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateTheme(_ theme: PresentationTheme) {
|
|
||||||
self.theme = theme
|
|
||||||
|
|
||||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
|
||||||
self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
|
|
||||||
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
|
||||||
self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
|
||||||
let backgroundInsets = self.backgroundInsets
|
|
||||||
let inputInsets = self.inputInsets
|
|
||||||
|
|
||||||
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
|
|
||||||
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
|
||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom))
|
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
|
||||||
|
|
||||||
let placeholderSize = self.placeholderNode.measure(backgroundFrame.size)
|
|
||||||
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
|
|
||||||
|
|
||||||
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - 20.0, height: backgroundFrame.size.height)))
|
|
||||||
|
|
||||||
return panelHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
func activateInput() {
|
|
||||||
self.textInputNode.becomeFirstResponder()
|
|
||||||
}
|
|
||||||
|
|
||||||
func deactivateInput() {
|
|
||||||
self.textInputNode.resignFirstResponder()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
|
||||||
self.updateTextNodeText(animated: true)
|
|
||||||
self.textChanged?(editableTextNode.textView.text)
|
|
||||||
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
||||||
let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text)
|
|
||||||
if updatedText.count > 1 {
|
|
||||||
self.textInputNode.layer.addShakeAnimation()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if text == "\n" {
|
|
||||||
self.complete?()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat {
|
|
||||||
let backgroundInsets = self.backgroundInsets
|
|
||||||
let inputInsets = self.inputInsets
|
|
||||||
|
|
||||||
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right - 20.0, height: CGFloat.greatestFiniteMagnitude)).height))
|
|
||||||
|
|
||||||
return min(61.0, max(33.0, unboundTextFieldHeight))
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateTextNodeText(animated: Bool) {
|
|
||||||
let backgroundInsets = self.backgroundInsets
|
|
||||||
|
|
||||||
let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width)
|
|
||||||
|
|
||||||
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
|
||||||
if !self.bounds.size.height.isEqual(to: panelHeight) {
|
|
||||||
self.updateHeight?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func clearPressed() {
|
|
||||||
self.placeholderNode.isHidden = false
|
|
||||||
|
|
||||||
self.textInputNode.attributedText = nil
|
|
||||||
self.updateHeight?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
|
private let dateTimeFormat: PresentationDateTimeFormat
|
||||||
private let title: String
|
private let title: String
|
||||||
private let text: String
|
private let text: String
|
||||||
|
private let placeholder: String
|
||||||
|
private let minValue: Int64
|
||||||
|
fileprivate var value: Int64
|
||||||
|
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ASTextNode
|
||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
let inputFieldNode: GiftAuctionCustomBidInputFieldNode
|
private let backgroundView = UIImageView()
|
||||||
|
let amountField = ComponentView<Empty>()
|
||||||
|
|
||||||
private let actionNodesSeparator: ASDisplayNode
|
private let actionNodesSeparator: ASDisplayNode
|
||||||
private let actionNodes: [TextAlertContentActionNode]
|
private let actionNodes: [TextAlertContentActionNode]
|
||||||
@ -182,7 +37,7 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
|
|
||||||
var complete: (() -> Void)? {
|
var complete: (() -> Void)? {
|
||||||
didSet {
|
didSet {
|
||||||
self.inputFieldNode.complete = self.complete
|
//self.inputFieldNode.complete = self.complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,18 +45,23 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
return self.isUserInteractionEnabled
|
return self.isUserInteractionEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: Int64) {
|
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, actions: [TextAlertAction], title: String, text: String, placeholder: String, minValue: Int64, value: Int64) {
|
||||||
|
self.theme = ptheme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.placeholder = placeholder
|
||||||
|
self.minValue = minValue
|
||||||
|
self.value = value
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ASTextNode()
|
||||||
self.titleNode.maximumNumberOfLines = 2
|
self.titleNode.maximumNumberOfLines = 2
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
self.textNode.maximumNumberOfLines = 8
|
self.textNode.maximumNumberOfLines = 8
|
||||||
|
|
||||||
self.inputFieldNode = GiftAuctionCustomBidInputFieldNode(theme: ptheme, placeholder: placeholder)
|
// self.inputFieldNode = GiftAuctionCustomBidInputFieldNode(theme: ptheme, placeholder: placeholder)
|
||||||
self.inputFieldNode.text = "\(value)"
|
// self.inputFieldNode.text = "\(value)"
|
||||||
|
|
||||||
self.actionNodesSeparator = ASDisplayNode()
|
self.actionNodesSeparator = ASDisplayNode()
|
||||||
self.actionNodesSeparator.isLayerBacked = true
|
self.actionNodesSeparator.isLayerBacked = true
|
||||||
@ -225,7 +85,7 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
self.addSubnode(self.inputFieldNode)
|
// self.addSubnode(self.inputFieldNode)
|
||||||
|
|
||||||
self.addSubnode(self.actionNodesSeparator)
|
self.addSubnode(self.actionNodesSeparator)
|
||||||
|
|
||||||
@ -237,14 +97,6 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
self.addSubnode(separatorNode)
|
self.addSubnode(separatorNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inputFieldNode.updateHeight = { [weak self] in
|
|
||||||
if let strongSelf = self {
|
|
||||||
if let _ = strongSelf.validLayout {
|
|
||||||
strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.updateTheme(theme)
|
self.updateTheme(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,10 +104,6 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
self.disposable.dispose()
|
self.disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
var value: String {
|
|
||||||
return self.inputFieldNode.text
|
|
||||||
}
|
|
||||||
|
|
||||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||||
self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||||
@ -327,13 +175,52 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
|
|
||||||
let resultWidth = contentWidth + insets.left + insets.right
|
let resultWidth = contentWidth + insets.left + insets.right
|
||||||
|
|
||||||
let inputFieldWidth = resultWidth
|
let fieldWidth = resultWidth - 18.0
|
||||||
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
|
let amountFieldSize = self.amountField.update(
|
||||||
let inputHeight = inputFieldHeight
|
transition: .immediate,
|
||||||
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight))
|
component: AnyComponent(
|
||||||
transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0)
|
AmountFieldComponent(
|
||||||
|
textColor: self.theme.list.itemPrimaryTextColor,
|
||||||
|
secondaryColor: self.theme.list.itemSecondaryTextColor,
|
||||||
|
placeholderColor: self.theme.list.itemPlaceholderTextColor,
|
||||||
|
accentColor: self.theme.list.itemAccentColor,
|
||||||
|
value: self.value,
|
||||||
|
minValue: self.minValue,
|
||||||
|
forceMinValue: false,
|
||||||
|
allowZero: false,
|
||||||
|
maxValue: nil,
|
||||||
|
placeholderText: self.placeholder,
|
||||||
|
textFieldOffset: CGPoint(x: -4.0, y: -1.0),
|
||||||
|
labelText: nil,
|
||||||
|
currency: .stars,
|
||||||
|
dateTimeFormat: self.dateTimeFormat,
|
||||||
|
amountUpdated: { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let value {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tag: nil
|
||||||
|
)
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: fieldWidth, height: 44.0)
|
||||||
|
)
|
||||||
|
let amountFieldFrame = CGRect(origin: CGPoint(x: floor((resultWidth - fieldWidth) / 2.0), y: origin.y - 2.0), size: amountFieldSize)
|
||||||
|
if let amountFieldView = self.amountField.view {
|
||||||
|
if amountFieldView.superview == nil {
|
||||||
|
self.backgroundView.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
||||||
|
|
||||||
let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom)
|
self.view.addSubview(self.backgroundView)
|
||||||
|
self.view.addSubview(amountFieldView)
|
||||||
|
}
|
||||||
|
self.backgroundView.frame = amountFieldFrame.insetBy(dx: 7.0, dy: 9.0)
|
||||||
|
amountFieldView.frame = amountFieldFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + amountFieldSize.height + actionsHeight + insets.top + insets.bottom + 3.0)
|
||||||
|
|
||||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
@ -381,19 +268,36 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !hadValidLayout {
|
if !hadValidLayout {
|
||||||
self.inputFieldNode.activateInput()
|
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
|
||||||
|
amountFieldView.activateInput()
|
||||||
|
amountFieldView.selectAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultSize
|
return resultSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateError() {
|
func animateError() {
|
||||||
self.inputFieldNode.layer.addShakeAnimation()
|
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
|
||||||
self.hapticFeedback.error()
|
self.value = self.minValue
|
||||||
|
if let size = self.validLayout {
|
||||||
|
_ = self.updateLayout(size: size, transition: .immediate)
|
||||||
|
}
|
||||||
|
amountFieldView.resetValue()
|
||||||
|
|
||||||
|
amountFieldView.animateError()
|
||||||
|
amountFieldView.selectAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func giftAuctionCustomBidController(context: AccountContext, title: String, text: String, placeholder: String, value: Int64, apply: @escaping (Int64) -> Void) -> AlertController {
|
func deactivateInput() {
|
||||||
|
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
|
||||||
|
amountFieldView.deactivateInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func giftAuctionCustomBidController(context: AccountContext, title: String, text: String, placeholder: String, action: String, minValue: Int64, value: Int64, apply: @escaping (Int64) -> Void) -> AlertController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
var dismissImpl: ((Bool) -> Void)?
|
var dismissImpl: ((Bool) -> Void)?
|
||||||
@ -401,11 +305,11 @@ func giftAuctionCustomBidController(context: AccountContext, title: String, text
|
|||||||
|
|
||||||
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
dismissImpl?(true)
|
dismissImpl?(true)
|
||||||
}), TextAlertAction(type: .defaultAction, title: "Place a Bid", action: {
|
}), TextAlertAction(type: .defaultAction, title: action, action: {
|
||||||
applyImpl?()
|
applyImpl?()
|
||||||
})]
|
})]
|
||||||
|
|
||||||
let contentNode = giftAuctionCustomBidAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value)
|
let contentNode = GiftAuctionCustomBidAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, actions: actions, title: title, text: text, placeholder: placeholder, minValue: minValue, value: value)
|
||||||
contentNode.complete = {
|
contentNode.complete = {
|
||||||
applyImpl?()
|
applyImpl?()
|
||||||
}
|
}
|
||||||
@ -413,23 +317,25 @@ func giftAuctionCustomBidController(context: AccountContext, title: String, text
|
|||||||
guard let contentNode = contentNode else {
|
guard let contentNode = contentNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let value = contentNode.value
|
||||||
|
if value < minValue {
|
||||||
|
contentNode.animateError()
|
||||||
|
} else {
|
||||||
dismissImpl?(true)
|
dismissImpl?(true)
|
||||||
|
apply(contentNode.value)
|
||||||
if let value = Int64(contentNode.value.trimmingCharacters(in: .whitespacesAndNewlines)) {
|
|
||||||
apply(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||||
let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in
|
let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
|
||||||
controller?.theme = AlertControllerTheme(presentationData: presentationData)
|
controller?.theme = AlertControllerTheme(presentationData: presentationData)
|
||||||
contentNode?.inputFieldNode.updateTheme(presentationData.theme)
|
|
||||||
})
|
})
|
||||||
controller.dismissed = { _ in
|
controller.dismissed = { _ in
|
||||||
presentationDataDisposable.dispose()
|
presentationDataDisposable.dispose()
|
||||||
}
|
}
|
||||||
dismissImpl = { [weak controller, weak contentNode] animated in
|
dismissImpl = { [weak controller, weak contentNode] animated in
|
||||||
contentNode?.inputFieldNode.deactivateInput()
|
contentNode?.deactivateInput()
|
||||||
|
let _ = contentNode
|
||||||
if animated {
|
if animated {
|
||||||
controller?.dismissAnimated()
|
controller?.dismissAnimated()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -33,20 +33,17 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let toPeerId: EnginePeer.Id
|
|
||||||
let auctionContext: GiftAuctionContext
|
let auctionContext: GiftAuctionContext
|
||||||
let animateOut: ActionSlot<Action<()>>
|
let animateOut: ActionSlot<Action<()>>
|
||||||
let getController: () -> ViewController?
|
let getController: () -> ViewController?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
toPeerId: EnginePeer.Id,
|
|
||||||
auctionContext: GiftAuctionContext,
|
auctionContext: GiftAuctionContext,
|
||||||
animateOut: ActionSlot<Action<()>>,
|
animateOut: ActionSlot<Action<()>>,
|
||||||
getController: @escaping () -> ViewController?
|
getController: @escaping () -> ViewController?
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.toPeerId = toPeerId
|
|
||||||
self.auctionContext = auctionContext
|
self.auctionContext = auctionContext
|
||||||
self.animateOut = animateOut
|
self.animateOut = animateOut
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
@ -63,7 +60,6 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
let averagePriceTag = GenericComponentViewTag()
|
let averagePriceTag = GenericComponentViewTag()
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let toPeerId: EnginePeer.Id
|
|
||||||
private let auctionContext: GiftAuctionContext
|
private let auctionContext: GiftAuctionContext
|
||||||
private let animateOut: ActionSlot<Action<()>>
|
private let animateOut: ActionSlot<Action<()>>
|
||||||
private let getController: () -> ViewController?
|
private let getController: () -> ViewController?
|
||||||
@ -82,13 +78,11 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
toPeerId: EnginePeer.Id,
|
|
||||||
auctionContext: GiftAuctionContext,
|
auctionContext: GiftAuctionContext,
|
||||||
animateOut: ActionSlot<Action<()>>,
|
animateOut: ActionSlot<Action<()>>,
|
||||||
getController: @escaping () -> ViewController?
|
getController: @escaping () -> ViewController?
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.toPeerId = toPeerId
|
|
||||||
self.auctionContext = auctionContext
|
self.auctionContext = auctionContext
|
||||||
self.animateOut = animateOut
|
self.animateOut = animateOut
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
@ -162,15 +156,16 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
}
|
}
|
||||||
|
|
||||||
func openAuction() {
|
func proceed() {
|
||||||
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController else {
|
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dismiss(animated: true)
|
self.dismiss(animated: true)
|
||||||
|
|
||||||
let bidController = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: self.auctionContext.currentBidPeerId ?? self.toPeerId, auctionContext: self.auctionContext)
|
controller.completion()
|
||||||
navigationController.pushViewController(bidController)
|
|
||||||
|
// let bidController = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: self.auctionContext.currentBidPeerId ?? self.toPeerId, auctionContext: self.auctionContext)
|
||||||
|
// navigationController.pushViewController(bidController)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openPeer(_ peer: EnginePeer, dismiss: Bool = true) {
|
func openPeer(_ peer: EnginePeer, dismiss: Bool = true) {
|
||||||
@ -350,7 +345,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeState() -> State {
|
func makeState() -> State {
|
||||||
return State(context: self.context, toPeerId: self.toPeerId, auctionContext: self.auctionContext, animateOut: self.animateOut, getController: self.getController)
|
return State(context: self.context, auctionContext: self.auctionContext, animateOut: self.animateOut, getController: self.getController)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
@ -765,7 +760,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||||||
guard let state else {
|
guard let state else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.openAuction()
|
state.proceed()
|
||||||
}),
|
}),
|
||||||
availableSize: buttonSize,
|
availableSize: buttonSize,
|
||||||
transition: .spring(duration: 0.2)
|
transition: .spring(duration: 0.2)
|
||||||
@ -964,16 +959,13 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
|
|||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let toPeerId: EnginePeer.Id
|
|
||||||
let auctionContext: GiftAuctionContext
|
let auctionContext: GiftAuctionContext
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
toPeerId: EnginePeer.Id,
|
|
||||||
auctionContext: GiftAuctionContext
|
auctionContext: GiftAuctionContext
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.toPeerId = toPeerId
|
|
||||||
self.auctionContext = auctionContext
|
self.auctionContext = auctionContext
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -998,7 +990,6 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
|
|||||||
component: SheetComponent<EnvironmentType>(
|
component: SheetComponent<EnvironmentType>(
|
||||||
content: AnyComponent<EnvironmentType>(GiftAuctionViewSheetContent(
|
content: AnyComponent<EnvironmentType>(GiftAuctionViewSheetContent(
|
||||||
context: context.component.context,
|
context: context.component.context,
|
||||||
toPeerId: context.component.toPeerId,
|
|
||||||
auctionContext: context.component.auctionContext,
|
auctionContext: context.component.auctionContext,
|
||||||
animateOut: animateOut,
|
animateOut: animateOut,
|
||||||
getController: controller
|
getController: controller
|
||||||
@ -1079,16 +1070,19 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
|
public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
|
||||||
|
fileprivate let completion: () -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
toPeerId: EnginePeer.Id,
|
auctionContext: GiftAuctionContext,
|
||||||
auctionContext: GiftAuctionContext
|
completion: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
|
self.completion = completion
|
||||||
|
|
||||||
super.init(
|
super.init(
|
||||||
context: context,
|
context: context,
|
||||||
component: GiftAuctionViewSheetComponent(
|
component: GiftAuctionViewSheetComponent(
|
||||||
context: context,
|
context: context,
|
||||||
toPeerId: toPeerId,
|
|
||||||
auctionContext: auctionContext
|
auctionContext: auctionContext
|
||||||
),
|
),
|
||||||
navigationBarAppearance: .none,
|
navigationBarAppearance: .none,
|
||||||
|
|||||||
@ -562,6 +562,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
accentColor: theme.list.itemAccentColor,
|
accentColor: theme.list.itemAccentColor,
|
||||||
value: state.amount?.value,
|
value: state.amount?.value,
|
||||||
minValue: minAmount?.value,
|
minValue: minAmount?.value,
|
||||||
|
forceMinValue: false,
|
||||||
allowZero: allowZero,
|
allowZero: allowZero,
|
||||||
maxValue: maxAmount?.value,
|
maxValue: maxAmount?.value,
|
||||||
placeholderText: amountPlaceholder,
|
placeholderText: amountPlaceholder,
|
||||||
@ -1296,6 +1297,7 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
|
|
||||||
private let textField: UITextField
|
private let textField: UITextField
|
||||||
private let minValue: Int64
|
private let minValue: Int64
|
||||||
|
private let forceMinValue: Bool
|
||||||
private let allowZero: Bool
|
private let allowZero: Bool
|
||||||
private let maxValue: Int64
|
private let maxValue: Int64
|
||||||
private let updated: (Int64) -> Void
|
private let updated: (Int64) -> Void
|
||||||
@ -1303,11 +1305,12 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
private let animateError: () -> Void
|
private let animateError: () -> Void
|
||||||
private let focusUpdated: (Bool) -> Void
|
private let focusUpdated: (Bool) -> Void
|
||||||
|
|
||||||
init?(textField: UITextField, currency: CurrencyAmount.Currency, dateTimeFormat: PresentationDateTimeFormat, minValue: Int64, allowZero: Bool, maxValue: Int64, updated: @escaping (Int64) -> Void, isEmptyUpdated: @escaping (Bool) -> Void, animateError: @escaping () -> Void, focusUpdated: @escaping (Bool) -> Void) {
|
init?(textField: UITextField, currency: CurrencyAmount.Currency, dateTimeFormat: PresentationDateTimeFormat, minValue: Int64, forceMinValue: Bool, allowZero: Bool, maxValue: Int64, updated: @escaping (Int64) -> Void, isEmptyUpdated: @escaping (Bool) -> Void, animateError: @escaping () -> Void, focusUpdated: @escaping (Bool) -> Void) {
|
||||||
self.textField = textField
|
self.textField = textField
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.minValue = minValue
|
self.minValue = minValue
|
||||||
|
self.forceMinValue = forceMinValue
|
||||||
self.allowZero = allowZero
|
self.allowZero = allowZero
|
||||||
self.maxValue = maxValue
|
self.maxValue = maxValue
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
@ -1434,7 +1437,17 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let amount: Int64 = self.amountFrom(text: newText)
|
let amount: Int64 = self.amountFrom(text: newText)
|
||||||
if amount > self.maxValue {
|
if self.forceMinValue && amount < self.minValue {
|
||||||
|
switch self.currency {
|
||||||
|
case .stars:
|
||||||
|
textField.text = "\(self.minValue)"
|
||||||
|
case .ton:
|
||||||
|
textField.text = "\(formatTonAmountText(self.minValue, dateTimeFormat: PresentationDateTimeFormat(timeFormat: self.dateTimeFormat.timeFormat, dateFormat: self.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: self.dateTimeFormat.decimalSeparator, groupingSeparator: ""), maxDecimalPositions: nil))"
|
||||||
|
}
|
||||||
|
self.onTextChanged(text: self.textField.text ?? "")
|
||||||
|
self.animateError()
|
||||||
|
return false
|
||||||
|
} else if amount > self.maxValue {
|
||||||
switch self.currency {
|
switch self.currency {
|
||||||
case .stars:
|
case .stars:
|
||||||
textField.text = "\(self.maxValue)"
|
textField.text = "\(self.maxValue)"
|
||||||
@ -1452,8 +1465,8 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class AmountFieldComponent: Component {
|
public final class AmountFieldComponent: Component {
|
||||||
typealias EnvironmentType = Empty
|
public typealias EnvironmentType = Empty
|
||||||
|
|
||||||
let textColor: UIColor
|
let textColor: UIColor
|
||||||
let secondaryColor: UIColor
|
let secondaryColor: UIColor
|
||||||
@ -1461,25 +1474,29 @@ private final class AmountFieldComponent: Component {
|
|||||||
let accentColor: UIColor
|
let accentColor: UIColor
|
||||||
let value: Int64?
|
let value: Int64?
|
||||||
let minValue: Int64?
|
let minValue: Int64?
|
||||||
|
let forceMinValue: Bool
|
||||||
let allowZero: Bool
|
let allowZero: Bool
|
||||||
let maxValue: Int64?
|
let maxValue: Int64?
|
||||||
let placeholderText: String
|
let placeholderText: String
|
||||||
|
let textFieldOffset: CGPoint
|
||||||
let labelText: String?
|
let labelText: String?
|
||||||
let currency: CurrencyAmount.Currency
|
let currency: CurrencyAmount.Currency
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let amountUpdated: (Int64?) -> Void
|
let amountUpdated: (Int64?) -> Void
|
||||||
let tag: AnyObject?
|
let tag: AnyObject?
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
textColor: UIColor,
|
textColor: UIColor,
|
||||||
secondaryColor: UIColor,
|
secondaryColor: UIColor,
|
||||||
placeholderColor: UIColor,
|
placeholderColor: UIColor,
|
||||||
accentColor: UIColor,
|
accentColor: UIColor,
|
||||||
value: Int64?,
|
value: Int64?,
|
||||||
minValue: Int64?,
|
minValue: Int64?,
|
||||||
|
forceMinValue: Bool,
|
||||||
allowZero: Bool,
|
allowZero: Bool,
|
||||||
maxValue: Int64?,
|
maxValue: Int64?,
|
||||||
placeholderText: String,
|
placeholderText: String,
|
||||||
|
textFieldOffset: CGPoint = .zero,
|
||||||
labelText: String?,
|
labelText: String?,
|
||||||
currency: CurrencyAmount.Currency,
|
currency: CurrencyAmount.Currency,
|
||||||
dateTimeFormat: PresentationDateTimeFormat,
|
dateTimeFormat: PresentationDateTimeFormat,
|
||||||
@ -1492,9 +1509,11 @@ private final class AmountFieldComponent: Component {
|
|||||||
self.accentColor = accentColor
|
self.accentColor = accentColor
|
||||||
self.value = value
|
self.value = value
|
||||||
self.minValue = minValue
|
self.minValue = minValue
|
||||||
|
self.forceMinValue = forceMinValue
|
||||||
self.allowZero = allowZero
|
self.allowZero = allowZero
|
||||||
self.maxValue = maxValue
|
self.maxValue = maxValue
|
||||||
self.placeholderText = placeholderText
|
self.placeholderText = placeholderText
|
||||||
|
self.textFieldOffset = textFieldOffset
|
||||||
self.labelText = labelText
|
self.labelText = labelText
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
@ -1502,7 +1521,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
self.tag = tag
|
self.tag = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: AmountFieldComponent, rhs: AmountFieldComponent) -> Bool {
|
public static func ==(lhs: AmountFieldComponent, rhs: AmountFieldComponent) -> Bool {
|
||||||
if lhs.textColor != rhs.textColor {
|
if lhs.textColor != rhs.textColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1539,7 +1558,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
final class View: UIView, UITextFieldDelegate, ComponentTaggedView {
|
public final class View: UIView, UITextFieldDelegate, ComponentTaggedView {
|
||||||
public func matches(tag: Any) -> Bool {
|
public func matches(tag: Any) -> Bool {
|
||||||
if let component = self.component, let componentTag = component.tag {
|
if let component = self.component, let componentTag = component.tag {
|
||||||
let tag = tag as AnyObject
|
let tag = tag as AnyObject
|
||||||
@ -1563,7 +1582,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
|
|
||||||
private var didSetValueOnce = false
|
private var didSetValueOnce = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
self.placeholderView = ComponentView<Empty>()
|
self.placeholderView = ComponentView<Empty>()
|
||||||
self.textField = TextFieldNodeView(frame: .zero)
|
self.textField = TextFieldNodeView(frame: .zero)
|
||||||
self.labelView = ComponentView<Empty>()
|
self.labelView = ComponentView<Empty>()
|
||||||
@ -1577,15 +1596,19 @@ private final class AmountFieldComponent: Component {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateInput() {
|
public func activateInput() {
|
||||||
self.textField.becomeFirstResponder()
|
self.textField.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectAll() {
|
public func deactivateInput() {
|
||||||
|
self.textField.resignFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func selectAll() {
|
||||||
self.textField.selectAll(nil)
|
self.textField.selectAll(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateError() {
|
public func animateError() {
|
||||||
self.textField.layer.addShakeAnimation()
|
self.textField.layer.addShakeAnimation()
|
||||||
let hapticFeedback = HapticFeedback()
|
let hapticFeedback = HapticFeedback()
|
||||||
hapticFeedback.error()
|
hapticFeedback.error()
|
||||||
@ -1594,6 +1617,13 @@ private final class AmountFieldComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func resetValue() {
|
||||||
|
guard let component = self.component, let value = component.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.textField.text = "\(value)"
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: AmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
func update(component: AmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||||
self.isUpdating = true
|
self.isUpdating = true
|
||||||
defer {
|
defer {
|
||||||
@ -1633,6 +1663,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
currency: component.currency,
|
currency: component.currency,
|
||||||
dateTimeFormat: component.dateTimeFormat,
|
dateTimeFormat: component.dateTimeFormat,
|
||||||
minValue: component.minValue ?? 0,
|
minValue: component.minValue ?? 0,
|
||||||
|
forceMinValue: component.forceMinValue,
|
||||||
allowZero: component.allowZero,
|
allowZero: component.allowZero,
|
||||||
maxValue: component.maxValue ?? Int64.max,
|
maxValue: component.maxValue ?? Int64.max,
|
||||||
updated: { [weak self] value in
|
updated: { [weak self] value in
|
||||||
@ -1669,6 +1700,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
currency: component.currency,
|
currency: component.currency,
|
||||||
dateTimeFormat: component.dateTimeFormat,
|
dateTimeFormat: component.dateTimeFormat,
|
||||||
minValue: component.minValue ?? 0,
|
minValue: component.minValue ?? 0,
|
||||||
|
forceMinValue: component.forceMinValue,
|
||||||
allowZero: component.allowZero,
|
allowZero: component.allowZero,
|
||||||
maxValue: component.maxValue ?? 10000000,
|
maxValue: component.maxValue ?? 10000000,
|
||||||
updated: { [weak self] value in
|
updated: { [weak self] value in
|
||||||
@ -1790,7 +1822,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
labelView.removeFromSuperview()
|
labelView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.textField.frame = CGRect(x: leftInset, y: 4.0, width: size.width - 30.0, height: 44.0)
|
self.textField.frame = CGRect(x: leftInset + component.textFieldOffset.x, y: 4.0 + component.textFieldOffset.y, width: size.width - 30.0, height: 44.0)
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
@ -1805,28 +1837,6 @@ private final class AmountFieldComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
|
||||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
|
|
||||||
context.setFillColor(backgroundColor.cgColor)
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
|
||||||
|
|
||||||
context.setLineWidth(2.0)
|
|
||||||
context.setLineCap(.round)
|
|
||||||
context.setStrokeColor(foregroundColor.cgColor)
|
|
||||||
|
|
||||||
context.move(to: CGPoint(x: 10.0, y: 10.0))
|
|
||||||
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
|
|
||||||
context.strokePath()
|
|
||||||
|
|
||||||
context.move(to: CGPoint(x: 20.0, y: 10.0))
|
|
||||||
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
|
|
||||||
context.strokePath()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct StarsWithdrawConfiguration {
|
private struct StarsWithdrawConfiguration {
|
||||||
static var defaultValue: StarsWithdrawConfiguration {
|
static var defaultValue: StarsWithdrawConfiguration {
|
||||||
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil, usdWithdrawRate: nil, tonUsdRate: nil)
|
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil, usdWithdrawRate: nil, tonUsdRate: nil)
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import TelegramStringFormatting
|
|||||||
import TextFormat
|
import TextFormat
|
||||||
import BrowserUI
|
import BrowserUI
|
||||||
import MediaEditorScreen
|
import MediaEditorScreen
|
||||||
|
import GiftSetupScreen
|
||||||
|
|
||||||
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
||||||
if case .default = navigation {
|
if case .default = navigation {
|
||||||
@ -1498,19 +1499,30 @@ func openResolvedUrlImpl(
|
|||||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
}
|
}
|
||||||
case let .auction(auctionContext):
|
case let .auction(auctionContext):
|
||||||
if let auctionContext {
|
if let auctionContext, case let .generic(gift) = auctionContext.gift {
|
||||||
if let currentBidPeerId = auctionContext.currentBidPeerId {
|
if let currentBidPeerId = auctionContext.currentBidPeerId {
|
||||||
let controller = context.sharedContext.makeGiftAuctionBidScreen(
|
let controller = context.sharedContext.makeGiftAuctionBidScreen(
|
||||||
context: context,
|
context: context,
|
||||||
toPeerId: currentBidPeerId,
|
toPeerId: currentBidPeerId,
|
||||||
|
text: nil,
|
||||||
|
entities: nil,
|
||||||
|
hideName: false,
|
||||||
auctionContext: auctionContext
|
auctionContext: auctionContext
|
||||||
)
|
)
|
||||||
navigationController?.pushViewController(controller)
|
navigationController?.pushViewController(controller)
|
||||||
} else {
|
} else {
|
||||||
let controller = context.sharedContext.makeGiftAuctionViewScreen(
|
let controller = context.sharedContext.makeGiftAuctionViewScreen(
|
||||||
context: context,
|
context: context,
|
||||||
toPeerId: context.account.peerId,
|
auctionContext: auctionContext,
|
||||||
auctionContext: auctionContext
|
completion: { [weak navigationController] in
|
||||||
|
let controller = GiftSetupScreen(
|
||||||
|
context: context,
|
||||||
|
peerId: context.account.peerId,
|
||||||
|
subject: .starGift(gift, nil),
|
||||||
|
completion: nil
|
||||||
|
)
|
||||||
|
navigationController?.pushViewController(controller)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
navigationController?.pushViewController(controller)
|
navigationController?.pushViewController(controller)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3846,12 +3846,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return GiftAuctionInfoScreen(context: context, auctionContext: auctionContext, completion: completion)
|
return GiftAuctionInfoScreen(context: context, auctionContext: auctionContext, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController {
|
public func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext) -> ViewController {
|
||||||
return GiftAuctionBidScreen(context: context, toPeerId: toPeerId, auctionContext: auctionContext)
|
return GiftAuctionBidScreen(context: context, toPeerId: toPeerId, text: text, entities: entities, hideName: hideName, auctionContext: auctionContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeGiftAuctionViewScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController {
|
public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping () -> Void) -> ViewController {
|
||||||
return GiftAuctionViewScreen(context: context, toPeerId: toPeerId, auctionContext: auctionContext)
|
return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController {
|
public func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user