Various improvements

This commit is contained in:
Ilya Laktyushin 2025-11-13 22:06:24 +04:00
parent 74e1d22de4
commit 5e17b321f8
19 changed files with 743 additions and 343 deletions

View File

@ -15167,7 +15167,12 @@ Error: %8$@";
"ScheduledMessages.Reminder.Delete" = "Delete Reminder";
"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.";
@ -15223,6 +15228,11 @@ Error: %8$@";
"Gift.AuctionBid.Top" = "TOP %@";
"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.CopyLink" = "Copy Link";
"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.View" = "View";
"Gift.Auction.Ongoing.Title" = "One Auction at a Time";
"Gift.Auction.Ongoing.Text" = "Youve already bid in this gift auction for **%@**.";
"Gift.Auction.Ongoing.TextYourself" = "Youve already bid in this gift auction for yourself.";
"Gift.Auction.Ongoing.View" = "View";
"Gift.Auction.Info.Title" = "Auction";
"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.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.OtherNoConvert" = "We'll notify %1$@ once it becomes eligible for unique upgrades.";
"Gift.View.NoConvertDescription" = "We'll notify you once it becomes eligible for unique upgrades.";
"Gift.View.OtherNoConvertDescription" = "We'll notify %1$@ 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" = "Display this gift on your page and turn it into a collectible.";
"Gift.View.NoConvertDescription" = "We will notify you 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";

View File

@ -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 makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController
func makeGiftAuctionViewScreen(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, auctionContext: GiftAuctionContext, completion: @escaping () -> Void) -> ViewController
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController

View File

@ -565,7 +565,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
guard let self, let auction else {
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)
})
}

View File

@ -20,7 +20,7 @@ public enum BotPaymentInvoiceSource {
case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool)
case starGiftPrepaidUpgrade(peerId: EnginePeer.Id, hash: String)
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 {
@ -426,22 +426,27 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv
return reference.apiStarGiftReference(transaction: transaction).flatMap { .inputInvoiceStarGiftDropOriginalDetails(stargift: $0) }
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
if hideName {
flags |= (1 << 0)
}
var inputPeer: Api.InputPeer?
var message: Api.TextWithEntities?
if update {
flags |= (1 << 2)
}
flags |= (1 << 3)
var message: Api.TextWithEntities?
if let text, !text.isEmpty {
flags |= (1 << 1)
message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? [])
if let peerId {
guard let peer = transaction.getPeer(peerId).flatMap(apiInputPeer) else {
return nil
}
flags |= (1 << 3)
inputPeer = peer
if hideName {
flags |= (1 << 0)
}
if let text, !text.isEmpty {
flags |= (1 << 1)
message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? [])
}
}
return .inputInvoiceStarGiftAuctionBid(flags: flags, peer: inputPeer, giftId: giftId, bidAmount: bidAmount, message: message)
}

View File

@ -464,6 +464,7 @@ swift_library(
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
"//submodules/TelegramUI/Components/Gifts/GiftStoreScreen",
"//submodules/TelegramUI/Components/Gifts/GiftSetupScreen",
"//submodules/TelegramUI/Components/ContentReportScreen",
"//submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen",
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",

View File

@ -1946,13 +1946,13 @@ private final class CameraScreenComponent: CombinedComponent {
view.animateIn()
}
}))
.disappear(ComponentTransition.Disappear({ view, transition, completion in
if let view = view as? CollageIconCarouselComponent.View, !transition.animation.isImmediate {
view.animateOut(completion: completion)
} else {
completion()
}
}))
.disappear(ComponentTransition.Disappear({ view, transition, completion in
if let view = view as? CollageIconCarouselComponent.View, !transition.animation.isImmediate {
view.animateOut(completion: completion)
} else {
completion()
}
}))
)
}
}

View File

@ -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):
var incoming = incoming
var convertStars = convertStars
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 {
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))
}
} else {
text = item.presentationData.strings.Notification_StarGift_Subtitle_OtherNoConvert(peerName).string
text = item.presentationData.strings.Notification_StarGift_Subtitle_OtherNoConvert
}
}
}

View File

@ -441,7 +441,7 @@ public final class GiftItemComponent: Component {
let _ = loadingBackground.update(
transition: transition,
component: AnyComponent(
ItemShimmeringLoadingComponent(color: component.theme.list.itemAccentColor, cornerRadius: 10.0)
ItemShimmeringLoadingComponent(color: component.theme.list.itemAccentColor, cornerRadius: cornerRadius)
),
environment: {},
containerSize: size

View File

@ -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
}

View File

@ -390,44 +390,47 @@ final class GiftOptionsScreenComponent: Component {
let giftController = component.context.sharedContext.makeGiftAuctionBidScreen(
context: component.context,
toPeerId: currentBidPeerId,
text: nil,
entities: nil,
hideName: false,
auctionContext: auctionContext
)
mainController.push(giftController)
} else {
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: currentBidPeerId))
|> deliverOnMainQueue).start(next: { [weak self, weak mainController] peer in
guard let component = self?.component, let environment = self?.environment, let mainController else {
let _ = (context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: currentBidPeerId),
TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId)
)
|> deliverOnMainQueue).start(next: { [weak self, weak mainController] fromPeer, toPeer in
guard let component = self?.component, let mainController, let fromPeer, let toPeer else {
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)
})
],
parseMarkdown: true
)
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)
})
mainController.present(alertController, in: .window(.root))
})
}
} else {
let giftController = component.context.sharedContext.makeGiftAuctionViewScreen(
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)
}

View File

@ -146,6 +146,11 @@ private final class GiftSetupScreenComponent: Component {
private var peerMap: [EnginePeer.Id: EnginePeer] = [:]
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 updateDisposable: Disposable?
@ -227,6 +232,8 @@ private final class GiftSetupScreenComponent: Component {
self.inputMediaNodeDataDisposable?.dispose()
self.updateDisposable?.dispose()
self.optionsDisposable?.dispose()
self.giftAuctionDisposable?.dispose()
self.giftAuctionTimer?.invalidate()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
@ -452,12 +459,11 @@ private final class GiftSetupScreenComponent: Component {
}
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
}
let context = component.context
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let peerId = component.peerId
var textInputText = NSAttributedString()
@ -466,6 +472,22 @@ private final class GiftSetupScreenComponent: Component {
}
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 perUserLimit: Int32?
var giftFile: TelegramMediaFile?
@ -879,6 +901,29 @@ private final class GiftSetupScreenComponent: Component {
if isSelfGift {
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?
if case let .starGift(gift, true) = component.subject, gift.upgradeStars != nil {
@ -1726,18 +1771,62 @@ private final class GiftSetupScreenComponent: Component {
}
contentHeight += remainingCountSize.height
contentHeight += 7.0
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
if self.cachedStarImage == nil || self.cachedStarImage?.1 !== environment.theme {
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
}
var buttonIsEnabled = true
let buttonString: String
switch component.subject {
@ -1763,19 +1852,76 @@ private final class GiftSetupScreenComponent: Component {
}
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
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 {
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.foregroundColor, value: environment.theme.list.itemCheckColors.foregroundColor, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.kern, value: 2.0, range: NSRange(range, in: buttonAttributedString.string))
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)
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(.foregroundColor, value: environment.theme.list.itemCheckColors.foregroundColor, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.kern, value: 2.0, range: NSRange(range, in: buttonAttributedString.string))
}
buttonTitleItems.append(AnyComponentWithIdentity(id: buttonString, component: AnyComponent(
MultilineTextComponent(text: .plain(buttonAttributedString))
)))
}
buttonTitleItems.append(AnyComponentWithIdentity(id: buttonString, component: AnyComponent(
MultilineTextComponent(text: .plain(buttonAttributedString))
)))
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 32.0)
let buttonHeight: CGFloat = 52.0
let actionButtonSize = self.actionButton.update(

View File

@ -49,6 +49,7 @@ swift_library(
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
"//submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen",
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
"//submodules/ActivityIndicator",
"//submodules/TelegramUI/Components/TabSelectorComponent",

View File

@ -332,7 +332,7 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
}
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)
})
}

View File

@ -940,17 +940,26 @@ private final class GiftAuctionBidScreenComponent: Component {
let context: AccountContext
let toPeerId: EnginePeer.Id
let text: String?
let entities: [MessageTextEntity]?
let hideName: Bool
let gift: StarGift
let auctionContext: GiftAuctionContext
init(
context: AccountContext,
toPeerId: EnginePeer.Id,
text: String?,
entities: [MessageTextEntity]?,
hideName: Bool,
gift: StarGift,
auctionContext: GiftAuctionContext
) {
self.context = context
self.toPeerId = toPeerId
self.text = text
self.entities = entities
self.hideName = hideName
self.gift = gift
self.auctionContext = auctionContext
}
@ -994,7 +1003,7 @@ private final class GiftAuctionBidScreenComponent: Component {
private static func makeSliderSteps(minRealValue: Int, maxRealValue: Int, isLogarithmic: Bool) -> [Int] {
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.insert(minRealValue, at: 0)
sliderSteps.removeAll(where: { $0 >= maxRealValue })
@ -1423,6 +1432,10 @@ private final class GiftAuctionBidScreenComponent: Component {
}
var isUpdate = false
var myBidPeerId: EnginePeer.Id?
if let peerId = self.giftAuctionState?.myState.bidPeerId {
myBidPeerId = peerId
}
if let myBidAmount = self.giftAuctionState?.myState.bidAmount {
isUpdate = true
if value == myBidAmount {
@ -1456,14 +1469,19 @@ private final class GiftAuctionBidScreenComponent: Component {
self.isLoading = true
self.state?.updated()
var peerId: EnginePeer.Id?
if !isUpdate || (myBidPeerId != nil && myBidPeerId != component.toPeerId) {
peerId = component.toPeerId
}
let source: BotPaymentInvoiceSource = .starGiftAuctionBid(
update: isUpdate,
hideName: false,
peerId: component.toPeerId,
hideName: peerId != nil ? component.hideName : false,
peerId: peerId,
giftId: gift.id,
bidAmount: value,
text: nil,
entities: nil
text: peerId != nil ? component.text : nil,
entities: peerId != nil ? component.entities : nil
)
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
@ -1489,7 +1507,7 @@ private final class GiftAuctionBidScreenComponent: Component {
self.isLoading = false
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 {
updatedAmount = updatedAmount.withMaxRealValue(newMaxValue)
}
@ -1682,15 +1700,33 @@ private final class GiftAuctionBidScreenComponent: Component {
}
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
}
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(
context: component.context,
title: "Place a Custom Bid",
text: "Description",
placeholder: "Bid",
value: 100,
title: environment.strings.Gift_AuctionBid_CustomBid_Title,
text: environment.strings.Gift_AuctionBid_CustomBid_Text("\(giftsPerRounds)").string,
placeholder: environment.strings.Gift_AuctionBid_CustomBid_Placeholder,
action: environment.strings.Gift_AuctionBid_CustomBid_Done,
minValue: minBidAmount,
value: minBidAmount,
apply: { [weak self] value in
guard let self else {
return
@ -2698,12 +2734,15 @@ public class GiftAuctionBidScreen: ViewControllerComponentContainer {
private var didPlayAppearAnimation: 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
super.init(context: context, component: GiftAuctionBidScreenComponent(
context: context,
toPeerId: toPeerId,
text: text,
entities: entities,
hideName: hideName,
gift: auctionContext.gift,
auctionContext: auctionContext
), navigationBarAppearance: .none, theme: .default)

View File

@ -7,168 +7,23 @@ import TelegramCore
import TelegramPresentationData
import AccountContext
import UrlEscaping
import ComponentFlow
import StarsWithdrawalScreen
private final class GiftAuctionCustomBidInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
private var 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 final class GiftAuctionCustomBidAlertContentNode: AlertContentNode {
private let theme: PresentationTheme
private let strings: PresentationStrings
private let dateTimeFormat: PresentationDateTimeFormat
private let title: String
private let text: String
private let placeholder: String
private let minValue: Int64
fileprivate var value: Int64
private let titleNode: ASTextNode
private let textNode: ASTextNode
let inputFieldNode: GiftAuctionCustomBidInputFieldNode
private let backgroundView = UIImageView()
let amountField = ComponentView<Empty>()
private let actionNodesSeparator: ASDisplayNode
private let actionNodes: [TextAlertContentActionNode]
@ -182,7 +37,7 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
var complete: (() -> Void)? {
didSet {
self.inputFieldNode.complete = self.complete
//self.inputFieldNode.complete = self.complete
}
}
@ -190,18 +45,23 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
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.dateTimeFormat = dateTimeFormat
self.title = title
self.text = text
self.placeholder = placeholder
self.minValue = minValue
self.value = value
self.titleNode = ASTextNode()
self.titleNode.maximumNumberOfLines = 2
self.textNode = ASTextNode()
self.textNode.maximumNumberOfLines = 8
self.inputFieldNode = GiftAuctionCustomBidInputFieldNode(theme: ptheme, placeholder: placeholder)
self.inputFieldNode.text = "\(value)"
// self.inputFieldNode = GiftAuctionCustomBidInputFieldNode(theme: ptheme, placeholder: placeholder)
// self.inputFieldNode.text = "\(value)"
self.actionNodesSeparator = ASDisplayNode()
self.actionNodesSeparator.isLayerBacked = true
@ -225,7 +85,7 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.inputFieldNode)
// self.addSubnode(self.inputFieldNode)
self.addSubnode(self.actionNodesSeparator)
@ -237,14 +97,6 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
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)
}
@ -252,10 +104,6 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
self.disposable.dispose()
}
var value: String {
return self.inputFieldNode.text
}
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 = 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 inputFieldWidth = resultWidth
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
let inputHeight = inputFieldHeight
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight))
transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0)
let fieldWidth = resultWidth - 18.0
let amountFieldSize = self.amountField.update(
transition: .immediate,
component: AnyComponent(
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)
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 + inputHeight + actionsHeight + insets.top + insets.bottom)
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)))
@ -381,19 +268,36 @@ private final class giftAuctionCustomBidAlertContentNode: AlertContentNode {
}
if !hadValidLayout {
self.inputFieldNode.activateInput()
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
amountFieldView.activateInput()
amountFieldView.selectAll()
}
}
return resultSize
}
func animateError() {
self.inputFieldNode.layer.addShakeAnimation()
self.hapticFeedback.error()
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
self.value = self.minValue
if let size = self.validLayout {
_ = self.updateLayout(size: size, transition: .immediate)
}
amountFieldView.resetValue()
amountFieldView.animateError()
amountFieldView.selectAll()
}
}
func deactivateInput() {
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
amountFieldView.deactivateInput()
}
}
}
func giftAuctionCustomBidController(context: AccountContext, title: String, text: String, placeholder: String, value: Int64, apply: @escaping (Int64) -> Void) -> AlertController {
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 }
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: {
dismissImpl?(true)
}), TextAlertAction(type: .defaultAction, title: "Place a Bid", action: {
}), TextAlertAction(type: .defaultAction, title: action, action: {
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 = {
applyImpl?()
}
@ -413,23 +317,25 @@ func giftAuctionCustomBidController(context: AccountContext, title: String, text
guard let contentNode = contentNode else {
return
}
dismissImpl?(true)
if let value = Int64(contentNode.value.trimmingCharacters(in: .whitespacesAndNewlines)) {
apply(value)
let value = contentNode.value
if value < minValue {
contentNode.animateError()
} else {
dismissImpl?(true)
apply(contentNode.value)
}
}
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)
contentNode?.inputFieldNode.updateTheme(presentationData.theme)
})
controller.dismissed = { _ in
presentationDataDisposable.dispose()
}
dismissImpl = { [weak controller, weak contentNode] animated in
contentNode?.inputFieldNode.deactivateInput()
contentNode?.deactivateInput()
let _ = contentNode
if animated {
controller?.dismissAnimated()
} else {

View File

@ -33,20 +33,17 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let toPeerId: EnginePeer.Id
let auctionContext: GiftAuctionContext
let animateOut: ActionSlot<Action<()>>
let getController: () -> ViewController?
init(
context: AccountContext,
toPeerId: EnginePeer.Id,
auctionContext: GiftAuctionContext,
animateOut: ActionSlot<Action<()>>,
getController: @escaping () -> ViewController?
) {
self.context = context
self.toPeerId = toPeerId
self.auctionContext = auctionContext
self.animateOut = animateOut
self.getController = getController
@ -63,7 +60,6 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
let averagePriceTag = GenericComponentViewTag()
private let context: AccountContext
private let toPeerId: EnginePeer.Id
private let auctionContext: GiftAuctionContext
private let animateOut: ActionSlot<Action<()>>
private let getController: () -> ViewController?
@ -82,13 +78,11 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
init(
context: AccountContext,
toPeerId: EnginePeer.Id,
auctionContext: GiftAuctionContext,
animateOut: ActionSlot<Action<()>>,
getController: @escaping () -> ViewController?
) {
self.context = context
self.toPeerId = toPeerId
self.auctionContext = auctionContext
self.animateOut = animateOut
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: {})
}
func openAuction() {
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController else {
func proceed() {
guard let controller = self.getController() as? GiftAuctionViewScreen else {
return
}
self.dismiss(animated: true)
let bidController = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: self.auctionContext.currentBidPeerId ?? self.toPeerId, auctionContext: self.auctionContext)
navigationController.pushViewController(bidController)
controller.completion()
// 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) {
@ -350,7 +345,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
}
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 {
@ -765,7 +760,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
guard let state else {
return
}
state.openAuction()
state.proceed()
}),
availableSize: buttonSize,
transition: .spring(duration: 0.2)
@ -964,16 +959,13 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let toPeerId: EnginePeer.Id
let auctionContext: GiftAuctionContext
init(
context: AccountContext,
toPeerId: EnginePeer.Id,
auctionContext: GiftAuctionContext
) {
self.context = context
self.toPeerId = toPeerId
self.auctionContext = auctionContext
}
@ -998,7 +990,6 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
component: SheetComponent<EnvironmentType>(
content: AnyComponent<EnvironmentType>(GiftAuctionViewSheetContent(
context: context.component.context,
toPeerId: context.component.toPeerId,
auctionContext: context.component.auctionContext,
animateOut: animateOut,
getController: controller
@ -1079,16 +1070,19 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
}
public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
fileprivate let completion: () -> Void
public init(
context: AccountContext,
toPeerId: EnginePeer.Id,
auctionContext: GiftAuctionContext
auctionContext: GiftAuctionContext,
completion: @escaping () -> Void
) {
self.completion = completion
super.init(
context: context,
component: GiftAuctionViewSheetComponent(
context: context,
toPeerId: toPeerId,
auctionContext: auctionContext
),
navigationBarAppearance: .none,

View File

@ -562,6 +562,7 @@ private final class SheetContent: CombinedComponent {
accentColor: theme.list.itemAccentColor,
value: state.amount?.value,
minValue: minAmount?.value,
forceMinValue: false,
allowZero: allowZero,
maxValue: maxAmount?.value,
placeholderText: amountPlaceholder,
@ -1296,6 +1297,7 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
private let textField: UITextField
private let minValue: Int64
private let forceMinValue: Bool
private let allowZero: Bool
private let maxValue: Int64
private let updated: (Int64) -> Void
@ -1303,11 +1305,12 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
private let animateError: () -> 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.currency = currency
self.dateTimeFormat = dateTimeFormat
self.minValue = minValue
self.forceMinValue = forceMinValue
self.allowZero = allowZero
self.maxValue = maxValue
self.updated = updated
@ -1434,7 +1437,17 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
}
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 {
case .stars:
textField.text = "\(self.maxValue)"
@ -1452,8 +1465,8 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
}
}
private final class AmountFieldComponent: Component {
typealias EnvironmentType = Empty
public final class AmountFieldComponent: Component {
public typealias EnvironmentType = Empty
let textColor: UIColor
let secondaryColor: UIColor
@ -1461,25 +1474,29 @@ private final class AmountFieldComponent: Component {
let accentColor: UIColor
let value: Int64?
let minValue: Int64?
let forceMinValue: Bool
let allowZero: Bool
let maxValue: Int64?
let placeholderText: String
let textFieldOffset: CGPoint
let labelText: String?
let currency: CurrencyAmount.Currency
let dateTimeFormat: PresentationDateTimeFormat
let amountUpdated: (Int64?) -> Void
let tag: AnyObject?
init(
public init(
textColor: UIColor,
secondaryColor: UIColor,
placeholderColor: UIColor,
accentColor: UIColor,
value: Int64?,
minValue: Int64?,
forceMinValue: Bool,
allowZero: Bool,
maxValue: Int64?,
placeholderText: String,
textFieldOffset: CGPoint = .zero,
labelText: String?,
currency: CurrencyAmount.Currency,
dateTimeFormat: PresentationDateTimeFormat,
@ -1492,9 +1509,11 @@ private final class AmountFieldComponent: Component {
self.accentColor = accentColor
self.value = value
self.minValue = minValue
self.forceMinValue = forceMinValue
self.allowZero = allowZero
self.maxValue = maxValue
self.placeholderText = placeholderText
self.textFieldOffset = textFieldOffset
self.labelText = labelText
self.currency = currency
self.dateTimeFormat = dateTimeFormat
@ -1502,7 +1521,7 @@ private final class AmountFieldComponent: Component {
self.tag = tag
}
static func ==(lhs: AmountFieldComponent, rhs: AmountFieldComponent) -> Bool {
public static func ==(lhs: AmountFieldComponent, rhs: AmountFieldComponent) -> Bool {
if lhs.textColor != rhs.textColor {
return false
}
@ -1539,7 +1558,7 @@ private final class AmountFieldComponent: Component {
return true
}
final class View: UIView, UITextFieldDelegate, ComponentTaggedView {
public final class View: UIView, UITextFieldDelegate, ComponentTaggedView {
public func matches(tag: Any) -> Bool {
if let component = self.component, let componentTag = component.tag {
let tag = tag as AnyObject
@ -1563,7 +1582,7 @@ private final class AmountFieldComponent: Component {
private var didSetValueOnce = false
override init(frame: CGRect) {
public override init(frame: CGRect) {
self.placeholderView = ComponentView<Empty>()
self.textField = TextFieldNodeView(frame: .zero)
self.labelView = ComponentView<Empty>()
@ -1577,15 +1596,19 @@ private final class AmountFieldComponent: Component {
fatalError("init(coder:) has not been implemented")
}
func activateInput() {
public func activateInput() {
self.textField.becomeFirstResponder()
}
func selectAll() {
self.textField.selectAll(nil)
public func deactivateInput() {
self.textField.resignFirstResponder()
}
func animateError() {
public func selectAll() {
self.textField.selectAll(nil)
}
public func animateError() {
self.textField.layer.addShakeAnimation()
let hapticFeedback = HapticFeedback()
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 {
self.isUpdating = true
defer {
@ -1633,6 +1663,7 @@ private final class AmountFieldComponent: Component {
currency: component.currency,
dateTimeFormat: component.dateTimeFormat,
minValue: component.minValue ?? 0,
forceMinValue: component.forceMinValue,
allowZero: component.allowZero,
maxValue: component.maxValue ?? Int64.max,
updated: { [weak self] value in
@ -1669,6 +1700,7 @@ private final class AmountFieldComponent: Component {
currency: component.currency,
dateTimeFormat: component.dateTimeFormat,
minValue: component.minValue ?? 0,
forceMinValue: component.forceMinValue,
allowZero: component.allowZero,
maxValue: component.maxValue ?? 10000000,
updated: { [weak self] value in
@ -1790,7 +1822,7 @@ private final class AmountFieldComponent: Component {
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
}
@ -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 {
static var defaultValue: StarsWithdrawConfiguration {
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil, usdWithdrawRate: nil, tonUsdRate: nil)

View File

@ -37,6 +37,7 @@ import TelegramStringFormatting
import TextFormat
import BrowserUI
import MediaEditorScreen
import GiftSetupScreen
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
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)
}
case let .auction(auctionContext):
if let auctionContext {
if let auctionContext, case let .generic(gift) = auctionContext.gift {
if let currentBidPeerId = auctionContext.currentBidPeerId {
let controller = context.sharedContext.makeGiftAuctionBidScreen(
context: context,
toPeerId: currentBidPeerId,
text: nil,
entities: nil,
hideName: false,
auctionContext: auctionContext
)
navigationController?.pushViewController(controller)
} else {
let controller = context.sharedContext.makeGiftAuctionViewScreen(
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)
}

View File

@ -3846,12 +3846,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return GiftAuctionInfoScreen(context: context, auctionContext: auctionContext, completion: completion)
}
public func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, auctionContext: GiftAuctionContext) -> ViewController {
return GiftAuctionBidScreen(context: context, toPeerId: toPeerId, auctionContext: auctionContext)
public func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext) -> ViewController {
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 {
return GiftAuctionViewScreen(context: context, toPeerId: toPeerId, auctionContext: auctionContext)
public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping () -> Void) -> ViewController {
return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: completion)
}
public func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController {