mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit 'b308f42396'
This commit is contained in:
commit
6dba110767
102 changed files with 6299 additions and 1449 deletions
|
|
@ -15434,3 +15434,72 @@ Error: %8$@";
|
|||
"LiveStream.ErrorMaxAllowedEmoji.Text_any" = "You can send up to %d emoji.";
|
||||
|
||||
"Stars.Purchase.StarGiftOfferInfo" = "Buy Stars to make a gift buy offer.";
|
||||
|
||||
"Notification.StarsGiftOffer.Offer" = "%1$@ offered you %2$@ for your gift %3$@";
|
||||
"Notification.StarsGiftOffer.Offer.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Offer.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.OfferYou" = "You offered %1$@ %2$@ for their gift %3$@";
|
||||
"Notification.StarsGiftOffer.OfferYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.OfferYou.Stars_any" = "%@ Stars";
|
||||
|
||||
"Notification.StarsGiftOffer.Accepted" = "%1$@ accepted your offer and sold you %2$@ for %3$@";
|
||||
"Notification.StarsGiftOffer.Accepted.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Accepted.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.AcceptedYou" = "You sold %1$@ to %2$@ for %3$@";
|
||||
"Notification.StarsGiftOffer.AcceptedYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.AcceptedYou.Stars_any" = "%@ Stars";
|
||||
|
||||
"Notification.StarsGiftOffer.Rejected" = "%1$@ rejected your offer for %2$@ – your %3$@ have been refunded";
|
||||
"Notification.StarsGiftOffer.Rejected.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Rejected.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.RejectedYou" = "You rejected %1$@'s offer";
|
||||
"Notification.StarsGiftOffer.RejectedYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.RejectedYou.Stars_any" = "%@ Stars";
|
||||
|
||||
"Notification.StarsGiftOffer.Expired" = "%1$@ didn't respond to your offer for %2$@ within %3$@ – your %4$@ have been refunded";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.ExpiredYou" = "The offer from %1$@ to buy your %2$@ for %3$@ has expired";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_any" = "%@ Stars";
|
||||
|
||||
"CreateExternalStream.RevokeStreamKey" = "Revoke Stream Key";
|
||||
"CreateExternalStream.Revoke.Text" = "Do you want to revoke the stream key?";
|
||||
"CreateExternalStream.Revoke.Revoke" = "Revoke";
|
||||
|
||||
"Gift.AuctionBid.UntilEnd" = "until the end";
|
||||
"Gift.AuctionBid.BeforeStart" = "before start";
|
||||
"Gift.AuctionBid.RoundSubtitle" = "Round %@ of %@";
|
||||
"Gift.AuctionBid.TopWinnersTotal" = "Top 3 of %@ Winners";
|
||||
|
||||
"Gift.Acquired.GiftRound" = "%1$@ in round %2$@";
|
||||
|
||||
"Gift.Options.Gift.Soon" = "soon";
|
||||
|
||||
"Chat.Auction.StartsIn" = "starts in ";
|
||||
"Chat.Auction.View" = "VIEW";
|
||||
"Chat.Auction.Upcoming" = "Upcoming Auction";
|
||||
|
||||
"Gift.Auction.Start" = "Start";
|
||||
"Gift.Auction.End" = "End";
|
||||
"Gift.Auction.Quantity" = "Quantity";
|
||||
"Gift.Auction.TotalRounds" = "Rounds";
|
||||
"Gift.Auction.TimeRound" = "Round %@";
|
||||
"Gift.Auction.TimeRounds" = "Rounds %@";
|
||||
"Gift.Auction.StartsInHours" = "auction starts in {h}:{m}:{s}";
|
||||
"Gift.Auction.StartsInMinutes" = "auction starts in {m}:{s}";
|
||||
"Gift.Auction.EarlyBid" = "Place an Early Bid";
|
||||
|
||||
"Gift.Auction.Hours_1" = "%@ hour";
|
||||
"Gift.Auction.Hours_any" = "%@ hours";
|
||||
"Gift.Auction.Minutes_1" = "%@ minute";
|
||||
"Gift.Auction.Minutes_any" = "%@ minutes";
|
||||
"Gift.Auction.Seconds_1" = "%@ second";
|
||||
"Gift.Auction.Seconds_any" = "%@ seconds";
|
||||
"Gift.Auction.HoursEach_1" = "%@ hour each";
|
||||
"Gift.Auction.HoursEach_any" = "%@ hours each";
|
||||
"Gift.Auction.MinutesEach_1" = "%@ minute each";
|
||||
"Gift.Auction.MinutesEach_any" = "%@ minutes each";
|
||||
"Gift.Auction.Extension" = "+ %1$@ for late bids in top %2$@";
|
||||
|
||||
"ChatList.Auctions.UpcomingAuction" = "Upcoming Auction";
|
||||
|
|
|
|||
|
|
@ -1422,11 +1422,13 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeStarsIntroScreen(context: AccountContext) -> ViewController
|
||||
func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: ((StarGift.UniqueGift) -> 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, attributes: [StarGift.UniqueGift.Attribute]?) -> ViewController
|
||||
func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController
|
||||
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
|
||||
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?) -> ViewController
|
||||
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>) -> Void) -> ViewController
|
||||
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
|
||||
func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
|
||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ enum BotCheckoutActionButtonState: Equatable {
|
|||
|
||||
private let titleFont = Font.semibold(17.0)
|
||||
|
||||
final class BotCheckoutActionButton: HighlightableButtonNode {
|
||||
final class BotCheckoutActionButton: HighlightTrackingButtonNode {
|
||||
static var height: CGFloat = 52.0
|
||||
|
||||
private var activeFillColor: UIColor
|
||||
private var inactiveFillColor: UIColor
|
||||
private var foregroundColor: UIColor
|
||||
|
||||
private let activeBackgroundNode: ASImageNode
|
||||
private let activeBackgroundNode: ASDisplayNode
|
||||
private var applePayButton: UIButton?
|
||||
private let labelNode: TextNode
|
||||
|
||||
|
|
@ -29,23 +29,17 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
|||
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
|
||||
private var activeImage: UIImage?
|
||||
private var inactiveImage: UIImage?
|
||||
|
||||
init(activeFillColor: UIColor, inactiveFillColor: UIColor, foregroundColor: UIColor) {
|
||||
self.activeFillColor = activeFillColor
|
||||
self.inactiveFillColor = inactiveFillColor
|
||||
self.foregroundColor = foregroundColor
|
||||
|
||||
let diameter: CGFloat = 20.0
|
||||
self.activeImage = generateStretchableFilledCircleImage(diameter: diameter, color: activeFillColor)
|
||||
self.inactiveImage = generateStretchableFilledCircleImage(diameter: diameter, color: inactiveFillColor)
|
||||
let diameter: CGFloat = 52.0
|
||||
|
||||
self.activeBackgroundNode = ASImageNode()
|
||||
self.activeBackgroundNode.displaysAsynchronously = false
|
||||
self.activeBackgroundNode.displayWithoutProcessing = true
|
||||
self.activeBackgroundNode = ASDisplayNode()
|
||||
self.activeBackgroundNode.isLayerBacked = true
|
||||
self.activeBackgroundNode.image = self.activeImage
|
||||
self.activeBackgroundNode.backgroundColor = activeFillColor
|
||||
self.activeBackgroundNode.cornerRadius = diameter / 2.0
|
||||
|
||||
self.labelNode = TextNode()
|
||||
self.labelNode.displaysAsynchronously = false
|
||||
|
|
@ -55,6 +49,21 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
|||
|
||||
self.addSubnode(self.activeBackgroundNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: highlighted ? 0.25 : 0.35, curve: .spring)
|
||||
if highlighted {
|
||||
let highlightedColor = self.activeFillColor.withMultiplied(hue: 1.0, saturation: 0.77, brightness: 1.01)
|
||||
transition.updateBackgroundColor(node: self.activeBackgroundNode, color: highlightedColor)
|
||||
transition.updateTransformScale(node: self, scale: 1.05)
|
||||
} else {
|
||||
transition.updateBackgroundColor(node: self.activeBackgroundNode, color: self.activeFillColor)
|
||||
transition.updateTransformScale(node: self, scale: 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setState(_ state: BotCheckoutActionButtonState) {
|
||||
|
|
@ -93,14 +102,8 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
|
|||
placeholderNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let image = isEnabled ? self.activeImage : self.inactiveImage
|
||||
if let image = image, let currentImage = self.activeBackgroundNode.image, currentImage !== image {
|
||||
self.activeBackgroundNode.image = image
|
||||
self.activeBackgroundNode.layer.animate(from: currentImage.cgImage! as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2)
|
||||
} else {
|
||||
self.activeBackgroundNode.image = image
|
||||
}
|
||||
|
||||
self.activeBackgroundNode.backgroundColor = isEnabled ? self.activeFillColor : self.inactiveFillColor
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: size, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let _ = labelApply()
|
||||
|
|
|
|||
|
|
@ -259,31 +259,31 @@ enum BotCheckoutEntry: ItemListNodeEntry {
|
|||
}
|
||||
})
|
||||
case let .paymentMethod(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openPaymentMethod()
|
||||
})
|
||||
case let .shippingInfo(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openInfo(.address(.street1))
|
||||
})
|
||||
case let .shippingMethod(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openShippingMethod()
|
||||
})
|
||||
case let .nameInfo(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openInfo(.name)
|
||||
})
|
||||
case let .emailInfo(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openInfo(.email)
|
||||
})
|
||||
case let .phoneInfo(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openInfo(.phone)
|
||||
})
|
||||
case let .actionPlaceholder(_, shimmeringIndex):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: " ", label: " ", sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: " ", label: " ", sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
}, shimmeringIndex: shimmeringIndex)
|
||||
}
|
||||
}
|
||||
|
|
@ -1245,7 +1245,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition, additionalInsets: UIEdgeInsets) {
|
||||
var updatedInsets = layout.intrinsicInsets
|
||||
|
||||
let bottomPanelHorizontalInset: CGFloat = 16.0
|
||||
let bottomPanelHorizontalInset: CGFloat = 30.0
|
||||
|
||||
var botName: String?
|
||||
if let botPeer = self.botPeerValue {
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ class BotCheckoutHeaderItemNode: ListViewItemNode {
|
|||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: true) : nil
|
||||
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: contentSize.height - separatorHeight), size: CGSize(width: params.width, height: separatorHeight))
|
||||
|
||||
|
|
|
|||
|
|
@ -141,13 +141,13 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
|||
let naturalContentHeight: CGFloat
|
||||
var verticalOffset: CGFloat = 0.0
|
||||
if item.isFinal {
|
||||
naturalContentHeight = 44.0
|
||||
naturalContentHeight = 52.0
|
||||
} else {
|
||||
switch neighbors.bottom {
|
||||
case .otherSection, .none:
|
||||
naturalContentHeight = 44.0
|
||||
naturalContentHeight = 52.0
|
||||
default:
|
||||
naturalContentHeight = 34.0
|
||||
naturalContentHeight = 42.0
|
||||
}
|
||||
}
|
||||
if let _ = previousItem as? BotCheckoutHeaderItem {
|
||||
|
|
@ -209,7 +209,7 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
|||
strongSelf.separatorNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: params.width - leftInset, height: UIScreenPixel))
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: true) : nil
|
||||
|
||||
strongSelf.bottomSeparatorNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: contentSize.height), size: CGSize(width: params.width, height: UIScreenPixel))
|
||||
|
|
|
|||
|
|
@ -70,15 +70,17 @@ final class BotPaymentActionItemNode: BotPaymentItemNode {
|
|||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: titleFont, textColor: theme.list.itemAccentColor)
|
||||
}
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: width - sideInset * 2.0, height: 44.0))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: 44.0 + UIScreenPixel)))
|
||||
let height: CGFloat = 52.0
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: width - sideInset * 2.0, height: height))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height + UIScreenPixel)))
|
||||
|
||||
let leftInset: CGFloat = 16.0
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: width - leftInset - 32.0 - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 11.0), size: titleSize))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 16.0), size: titleSize))
|
||||
|
||||
return 44.0
|
||||
return height
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ final class BotPaymentCardInputItemNode: BotPaymentItemNode, STPPaymentCardTextF
|
|||
self.cardField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
|
||||
}
|
||||
|
||||
self.cardField.frame = CGRect(origin: CGPoint(x: 5.0 + sideInset, y: 0.0), size: CGSize(width: width - 10.0 - sideInset * 2.0, height: 44.0))
|
||||
self.cardField.frame = CGRect(origin: CGPoint(x: 5.0 + sideInset, y: 4.0), size: CGSize(width: width - 10.0 - sideInset * 2.0, height: 44.0))
|
||||
|
||||
return 44.0
|
||||
return 52.0
|
||||
}
|
||||
|
||||
func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
|
||||
|
|
|
|||
|
|
@ -102,13 +102,15 @@ class BotPaymentDisclosureItemNode: BotPaymentItemNode {
|
|||
}
|
||||
}
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: width - sideInset * 2.0, height: 44.0))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: 44.0 + UIScreenPixel)))
|
||||
let height: CGFloat = 52.0
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: width - sideInset * 2.0, height: height))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height + UIScreenPixel)))
|
||||
|
||||
let leftInset: CGFloat = 16.0
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: width - leftInset - 70.0 - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 11.0), size: titleSize))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 16.0), size: titleSize))
|
||||
|
||||
var textInset = leftInset
|
||||
if !titleSize.width.isZero {
|
||||
|
|
@ -117,9 +119,9 @@ class BotPaymentDisclosureItemNode: BotPaymentItemNode {
|
|||
textInset = max(measuredInset, textInset)
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: width - measuredInset - 8.0 - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: textInset + sideInset, y: 11.0), size: textSize))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: textInset + sideInset, y: 16.0), size: textSize))
|
||||
|
||||
return 44.0
|
||||
return height
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
|
|
|
|||
|
|
@ -108,11 +108,12 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate {
|
|||
self.textField.textField.tintColor = theme.list.itemAccentColor
|
||||
}
|
||||
|
||||
let height: CGFloat = 52.0
|
||||
let leftInset: CGFloat = 16.0
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: width - leftInset - 70.0 - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 11.0), size: titleSize))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 16.0), size: titleSize))
|
||||
|
||||
var textInset = leftInset
|
||||
if !titleSize.width.isZero {
|
||||
|
|
@ -121,9 +122,9 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate {
|
|||
|
||||
textInset = max(measuredInset, textInset)
|
||||
|
||||
transition.updateFrame(node: self.textField, frame: CGRect(origin: CGPoint(x: textInset + sideInset, y: 0.0), size: CGSize(width: max(1.0, width - textInset - 8.0), height: 40.0)))
|
||||
transition.updateFrame(node: self.textField, frame: CGRect(origin: CGPoint(x: textInset + sideInset, y: 5.0), size: CGSize(width: max(1.0, width - textInset - 8.0), height: 40.0)))
|
||||
|
||||
return 44.0
|
||||
return height
|
||||
}
|
||||
|
||||
func activateInput() {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class BotPaymentItemNode: ASDisplayNode {
|
|||
self.bottomSeparatorNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(theme, top: hasTopCorners, bottom: hasBottomCorners, glass: true) : nil
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height)))
|
||||
transition.updateFrame(node: self.maskNode, frame: self.backgroundNode.frame.insetBy(dx: sideInset, dy: 0.0))
|
||||
|
|
|
|||
|
|
@ -74,14 +74,14 @@ final class BotPaymentSwitchItemNode: BotPaymentItemNode {
|
|||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: width - leftInset - 70.0 - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 11.0), size: titleSize))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: 16.0), size: titleSize))
|
||||
|
||||
let switchSize = self.switchNode.measure(CGSize(width: 100.0, height: 100.0))
|
||||
let switchFrame = CGRect(origin: CGPoint(x: width - switchSize.width - 15.0 - sideInset, y: 6.0), size: switchSize)
|
||||
let switchFrame = CGRect(origin: CGPoint(x: width - switchSize.width - 15.0 - sideInset, y: 12.0), size: switchSize)
|
||||
transition.updateFrame(node: self.switchNode, frame: switchFrame)
|
||||
transition.updateFrame(node: self.buttonNode, frame: switchFrame)
|
||||
|
||||
return 44.0
|
||||
return 52.0
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ final class BotReceiptControllerNode: ItemListControllerNode {
|
|||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition, additionalInsets: UIEdgeInsets) {
|
||||
var updatedInsets = layout.intrinsicInsets
|
||||
|
||||
let bottomPanelHorizontalInset: CGFloat = 16.0
|
||||
let bottomPanelHorizontalInset: CGFloat = 30.0
|
||||
let bottomPanelVerticalInset: CGFloat = 16.0
|
||||
let bottomPanelHeight = max(updatedInsets.bottom, layout.inputHeight ?? 0.0) + bottomPanelVerticalInset * 2.0 + BotCheckoutActionButton.height
|
||||
|
||||
|
|
|
|||
|
|
@ -1425,9 +1425,13 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
var nodeList = document.getElementsByTagName('link');
|
||||
for (var i = 0; i < nodeList.length; i++)
|
||||
{
|
||||
if((nodeList[i].getAttribute('rel') == 'icon')||(nodeList[i].getAttribute('rel') == 'shortcut icon')||(nodeList[i].getAttribute('rel').startsWith('apple-touch-icon')))
|
||||
{
|
||||
const node = nodeList[i];
|
||||
var rel = nodeList[i].getAttribute('rel') || '';
|
||||
if (
|
||||
rel === 'icon' ||
|
||||
rel === 'shortcut icon' ||
|
||||
rel.indexOf('apple-touch-icon') === 0
|
||||
) {
|
||||
var node = nodeList[i];
|
||||
favicons.push({
|
||||
url: node.getAttribute('href'),
|
||||
sizes: node.getAttribute('sizes')
|
||||
|
|
|
|||
|
|
@ -157,40 +157,6 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
}
|
||||
}
|
||||
|
||||
final class BackgroundView: UIView {
|
||||
let topCornersView = UIView()
|
||||
let bottomCornersView = UIView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.topCornersView.clipsToBounds = true
|
||||
self.topCornersView.layer.cornerCurve = .continuous
|
||||
self.topCornersView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.bottomCornersView.clipsToBounds = true
|
||||
self.bottomCornersView.layer.cornerCurve = .continuous
|
||||
self.bottomCornersView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
|
||||
self.addSubview(self.topCornersView)
|
||||
self.topCornersView.addSubview(self.bottomCornersView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(size: CGSize, color: UIColor, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, transition: ComponentTransition) {
|
||||
transition.setCornerRadius(layer: self.topCornersView.layer, cornerRadius: topCornerRadius)
|
||||
transition.setCornerRadius(layer: self.bottomCornersView.layer, cornerRadius: bottomCornerRadius)
|
||||
|
||||
transition.setFrame(view: self.topCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.setFrame(view: self.bottomCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
transition.setBackgroundColor(view: self.bottomCornersView, color: color)
|
||||
}
|
||||
}
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
|
||||
public final class Tag {
|
||||
public init() {
|
||||
|
|
@ -208,9 +174,9 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
|
||||
private let dimView: UIView
|
||||
private let scrollView: ScrollView
|
||||
private let backgroundView: BackgroundView
|
||||
private let backgroundView: SheetBackgroundView
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let clipView: BackgroundView
|
||||
private let clipView: SheetBackgroundView
|
||||
private let contentView: ComponentView<ChildEnvironmentType>
|
||||
private var headerView: ComponentView<Empty>?
|
||||
|
||||
|
|
@ -233,8 +199,8 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
|
||||
self.backgroundView = BackgroundView()
|
||||
self.clipView = BackgroundView()
|
||||
self.backgroundView = SheetBackgroundView()
|
||||
self.clipView = SheetBackgroundView()
|
||||
|
||||
self.contentView = ComponentView<ChildEnvironmentType>()
|
||||
|
||||
|
|
@ -589,3 +555,37 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class SheetBackgroundView: UIView {
|
||||
let topCornersView = UIView()
|
||||
let bottomCornersView = UIView()
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.topCornersView.clipsToBounds = true
|
||||
self.topCornersView.layer.cornerCurve = .continuous
|
||||
self.topCornersView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.bottomCornersView.clipsToBounds = true
|
||||
self.bottomCornersView.layer.cornerCurve = .continuous
|
||||
self.bottomCornersView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
|
||||
self.addSubview(self.topCornersView)
|
||||
self.topCornersView.addSubview(self.bottomCornersView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
public func update(size: CGSize, color: UIColor, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, transition: ComponentTransition) {
|
||||
transition.setCornerRadius(layer: self.topCornersView.layer, cornerRadius: topCornerRadius)
|
||||
transition.setCornerRadius(layer: self.bottomCornersView.layer, cornerRadius: bottomCornerRadius)
|
||||
|
||||
transition.setFrame(view: self.topCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.setFrame(view: self.bottomCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
transition.setBackgroundColor(view: self.bottomCornersView, color: color)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ class GeneralChartComponentController: ChartThemeContainer {
|
|||
values.append(ChartDetailsViewModel.Value(
|
||||
prefix: nil,
|
||||
title: self.strings.revenueInUsd,
|
||||
value: "≈$\(convertedValueString)",
|
||||
value: "~$\(convertedValueString)",
|
||||
color: color,
|
||||
visible: firstValue.visible
|
||||
))
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class BarsComponentController: GeneralChartComponentController {
|
|||
} else {
|
||||
text = String(format: "%0.3f", convertedValue)
|
||||
}
|
||||
updatedLabels.append(LinesChartLabel(value: label.value, text: "≈$\(text)"))
|
||||
updatedLabels.append(LinesChartLabel(value: label.value, text: "~$\(text)"))
|
||||
}
|
||||
labels = updatedLabels
|
||||
}
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
|||
var label: String = ""
|
||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > StarsAmount.zero {
|
||||
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("~\(formatTonUsdValue(subscriptionFee.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
}
|
||||
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, StarsAmount(value: configuration.maxFee, nanos: 0)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public final class ListMessageItemInteraction {
|
|||
})
|
||||
}
|
||||
|
||||
public final class ListMessageItem: ListViewItem {
|
||||
public final class ListMessageItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ChatPresentationData
|
||||
let systemStyle: ItemListSystemStyle
|
||||
let context: AccountContext
|
||||
|
|
@ -57,6 +57,7 @@ public final class ListMessageItem: ListViewItem {
|
|||
let isGlobalSearchResult: Bool
|
||||
let isDownloadList: Bool
|
||||
let isSavedMusic: Bool
|
||||
let isStoryMusic: Bool
|
||||
let displayFileInfo: Bool
|
||||
let displayBackground: Bool
|
||||
let canReorder: Bool
|
||||
|
|
@ -64,9 +65,11 @@ public final class ListMessageItem: ListViewItem {
|
|||
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
public var sectionId: ItemListSectionId
|
||||
|
||||
public let selectable: Bool = true
|
||||
|
||||
public init(presentationData: ChatPresentationData, systemStyle: ItemListSystemStyle = .legacy, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message?, translateToLanguage: String? = nil, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false, isDownloadList: Bool = false, isSavedMusic: Bool = false, displayFileInfo: Bool = true, displayBackground: Bool = false, canReorder: Bool = false, style: ItemListStyle = .plain) {
|
||||
public init(presentationData: ChatPresentationData, systemStyle: ItemListSystemStyle = .legacy, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message?, translateToLanguage: String? = nil, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false, isDownloadList: Bool = false, isSavedMusic: Bool = false, isStoryMusic: Bool = false, displayFileInfo: Bool = true, displayBackground: Bool = false, canReorder: Bool = false, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0) {
|
||||
self.presentationData = presentationData
|
||||
self.systemStyle = systemStyle
|
||||
self.context = context
|
||||
|
|
@ -86,10 +89,12 @@ public final class ListMessageItem: ListViewItem {
|
|||
self.isGlobalSearchResult = isGlobalSearchResult
|
||||
self.isDownloadList = isDownloadList
|
||||
self.isSavedMusic = isSavedMusic
|
||||
self.isStoryMusic = isStoryMusic
|
||||
self.displayFileInfo = displayFileInfo
|
||||
self.displayBackground = displayBackground
|
||||
self.canReorder = canReorder
|
||||
self.style = style
|
||||
self.sectionId = sectionId
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
|
|
@ -120,7 +125,23 @@ public final class ListMessageItem: ListViewItem {
|
|||
node.setupItem(self)
|
||||
|
||||
let nodeLayout = node.asyncLayout()
|
||||
let (top, bottom, dateAtBottom) = (previousItem != nil && !(previousItem is ItemListItem), nextItem != nil, self.getDateAtBottom(top: previousItem, bottom: nextItem))
|
||||
|
||||
var topMerged = false
|
||||
if let previousItem {
|
||||
if let previousItem = previousItem as? ItemListItem, previousItem.sectionId == self.sectionId && !previousItem.isAlwaysPlain {
|
||||
topMerged = true
|
||||
}
|
||||
}
|
||||
|
||||
var bottomMerged = false
|
||||
if let nextItem {
|
||||
if let nextItem = nextItem as? ItemListItem, nextItem.sectionId == self.sectionId && !nextItem.isAlwaysPlain {
|
||||
bottomMerged = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let (top, bottom, dateAtBottom) = (topMerged, bottomMerged, self.getDateAtBottom(top: previousItem, bottom: nextItem))
|
||||
let (layout, apply) = nodeLayout(self, params, top, bottom, dateAtBottom)
|
||||
|
||||
node.updateSelectionState(animated: false)
|
||||
|
|
@ -152,9 +173,22 @@ public final class ListMessageItem: ListViewItem {
|
|||
|
||||
let nodeLayout = nodeValue.asyncLayout()
|
||||
|
||||
var topMerged = false
|
||||
if let previousItem {
|
||||
if let previousItem = previousItem as? ItemListItem, previousItem.sectionId == self.sectionId && !previousItem.isAlwaysPlain {
|
||||
topMerged = true
|
||||
}
|
||||
}
|
||||
|
||||
var bottomMerged = false
|
||||
if let nextItem {
|
||||
if let nextItem = nextItem as? ItemListItem, nextItem.sectionId == self.sectionId && !nextItem.isAlwaysPlain {
|
||||
bottomMerged = true
|
||||
}
|
||||
}
|
||||
|
||||
async {
|
||||
let (top, bottom, dateAtBottom) = (previousItem != nil && !(previousItem is ItemListItem), nextItem != nil, self.getDateAtBottom(top: previousItem, bottom: nextItem))
|
||||
|
||||
let (top, bottom, dateAtBottom) = (topMerged, bottomMerged, self.getDateAtBottom(top: previousItem, bottom: nextItem))
|
||||
let (layout, apply) = nodeLayout(self, params, top, bottom, dateAtBottom)
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
|
|
|
|||
|
|
@ -446,9 +446,9 @@ public func storyLocationPickerController(
|
|||
) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false)
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, style: .glass, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false)
|
||||
controller.requestController = { _, present in
|
||||
let locationPickerController = LocationPickerController(context: context, updatedPresentationData: updatedPresentationData, mode: .share(peer: nil, selfPeer: nil, hasLiveLocation: false), source: .story, initialLocation: location, completion: { location, queryId, resultId, address, countryCode in
|
||||
let locationPickerController = LocationPickerController(context: context, style: .glass, updatedPresentationData: updatedPresentationData, mode: .share(peer: nil, selfPeer: nil, hasLiveLocation: false), source: .story, initialLocation: location, completion: { location, queryId, resultId, address, countryCode in
|
||||
completion(location, queryId, resultId, address, countryCode)
|
||||
})
|
||||
present(locationPickerController, locationPickerController.mediaPickerContext)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public func mediaPasteboardScreen(
|
|||
getSourceRect: (() -> CGRect?)? = nil,
|
||||
makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil }
|
||||
) -> ViewController {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: peer.id), buttons: [.standalone], initialButton: .standalone, makeEntityInputView: makeEntityInputView)
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, style: .glass, chatLocation: .peer(id: peer.id), buttons: [.standalone], initialButton: .standalone, makeEntityInputView: makeEntityInputView)
|
||||
controller.requestController = { _, present in
|
||||
presentMediaPicker(.media(subjects), false, nil, nil, { mediaPicker, mediaPickerContext in
|
||||
present(mediaPicker, mediaPickerContext)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ swift_library(
|
|||
"//submodules/Components/ActivityIndicatorComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ import BundleIconComponent
|
|||
import AnimatedStickerComponent
|
||||
import ActivityIndicatorComponent
|
||||
import GlassBarButtonComponent
|
||||
import ListSectionComponent
|
||||
import ListActionItemComponent
|
||||
import PlainButtonComponent
|
||||
|
||||
private final class CreateExternalMediaStreamScreenComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -49,41 +52,51 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
|||
final class State: ComponentState {
|
||||
let context: AccountContext
|
||||
let peerId: EnginePeer.Id
|
||||
let mode: CreateExternalMediaStreamScreen.Mode
|
||||
|
||||
private(set) var credentials: GroupCallStreamCredentials?
|
||||
var isDelayingLoadingIndication: Bool = true
|
||||
|
||||
private var credentialsDisposable: Disposable?
|
||||
private let credentialsDisposable = MetaDisposable()
|
||||
private let activeActionDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, peerId: EnginePeer.Id, credentialsPromise: Promise<GroupCallStreamCredentials>?) {
|
||||
init(context: AccountContext, peerId: EnginePeer.Id, mode: CreateExternalMediaStreamScreen.Mode, credentialsPromise: Promise<GroupCallStreamCredentials>?) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.mode = mode
|
||||
|
||||
super.init()
|
||||
|
||||
self.getCredentials(credentialsPromise: credentialsPromise)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.credentialsDisposable.dispose()
|
||||
self.activeActionDisposable.dispose()
|
||||
}
|
||||
|
||||
func getCredentials(credentialsPromise: Promise<GroupCallStreamCredentials>? = nil, revoke: Bool = false) {
|
||||
let credentialsSignal: Signal<GroupCallStreamCredentials, NoError>
|
||||
if let credentialsPromise = credentialsPromise {
|
||||
credentialsSignal = credentialsPromise.get()
|
||||
} else {
|
||||
credentialsSignal = context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false)
|
||||
var isLiveStream = false
|
||||
if case let .create(isLiveStreamValue) = self.mode {
|
||||
isLiveStream = isLiveStreamValue
|
||||
}
|
||||
credentialsSignal = self.context.engine.calls.getGroupCallStreamCredentials(peerId: self.peerId, isLiveStream: isLiveStream, revokePreviousCredentials: revoke)
|
||||
|> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
self.credentialsDisposable = (credentialsSignal |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
self.credentialsDisposable.set((credentialsSignal |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.credentials = result
|
||||
strongSelf.updated(transition: .immediate)
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.credentialsDisposable?.dispose()
|
||||
self.activeActionDisposable.dispose()
|
||||
}))
|
||||
}
|
||||
|
||||
func copyCredentials(_ key: KeyPath<GroupCallStreamCredentials, String>) {
|
||||
|
|
@ -160,7 +173,7 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
|||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State(context: self.context, peerId: self.peerId, credentialsPromise: self.credentialsPromise)
|
||||
return State(context: self.context, peerId: self.peerId, mode: self.mode, credentialsPromise: self.credentialsPromise)
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
|
|
@ -176,26 +189,25 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
|||
|
||||
let activityIndicator = Child(ActivityIndicatorComponent.self)
|
||||
|
||||
let credentialsBackground = Child(RoundedRectangle.self)
|
||||
let credentialsSection = Child(ListSectionComponent.self)
|
||||
|
||||
let credentialsStripe = Child(Rectangle.self)
|
||||
let credentialsURLTitle = Child(MultilineTextComponent.self)
|
||||
let credentialsURLText = Child(MultilineTextComponent.self)
|
||||
|
||||
let credentialsKeyTitle = Child(MultilineTextComponent.self)
|
||||
let credentialsKeyText = Child(MultilineTextComponent.self)
|
||||
|
||||
let credentialsCopyURLButton = Child(Button.self)
|
||||
let credentialsCopyKeyButton = Child(Button.self)
|
||||
// let credentialsBackground = Child(RoundedRectangle.self)
|
||||
// let credentialsStripe = Child(Rectangle.self)
|
||||
// let credentialsURLTitle = Child(MultilineTextComponent.self)
|
||||
// let credentialsURLText = Child(MultilineTextComponent.self)
|
||||
//
|
||||
// let credentialsKeyTitle = Child(MultilineTextComponent.self)
|
||||
// let credentialsKeyText = Child(MultilineTextComponent.self)
|
||||
//
|
||||
// let credentialsCopyURLButton = Child(Button.self)
|
||||
// let credentialsCopyKeyButton = Child(Button.self)
|
||||
|
||||
return { context in
|
||||
let topInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0
|
||||
let buttonSideInset: CGFloat = 36.0
|
||||
let credentialsSideInset: CGFloat = 16.0
|
||||
let credentialsTopInset: CGFloat = 12.0
|
||||
let credentialsTitleSpacing: CGFloat = 5.0
|
||||
|
||||
let component = context.component
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let state = context.state
|
||||
|
||||
|
|
@ -355,124 +367,145 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
|||
)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - text.size.width) / 2.0), y: animationFrame.maxY + 18.0), size: text.size)
|
||||
|
||||
context.add(text
|
||||
.position(CGPoint(x: textFrame.midX, y: textFrame.midY))
|
||||
)
|
||||
|
||||
let credentialsFrame = CGRect(origin: CGPoint(x: sideInset, y: textFrame.maxY + 30.0), size: credentialsAreaSize)
|
||||
|
||||
if let credentials = context.state.credentials {
|
||||
let credentialsURLTitle = credentialsURLTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.CreateExternalStream_ServerUrl, font: Font.regular(15.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: credentialsAreaSize.width - credentialsSideInset * 2.0, height: credentialsAreaSize.height),
|
||||
transition: context.transition
|
||||
var credentialsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
credentialsSectionItems.append(
|
||||
AnyComponentWithIdentity(id: "url", component: AnyComponent(
|
||||
ListActionItemComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: environment.strings.CreateExternalStream_ServerUrl,
|
||||
font: Font.regular(15.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: credentials.url, font: Font.regular(17.0), textColor: theme.list.itemAccentColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
truncationType: .middle,
|
||||
maximumNumberOfLines: 1
|
||||
)))
|
||||
], alignment: .left, spacing: 5.0)),
|
||||
contentInsets: UIEdgeInsets(top: 14.0, left: 0.0, bottom: 14.0, right: 0.0),
|
||||
accessory: .custom(ListActionItemComponent.CustomAccessory(
|
||||
component: AnyComponentWithIdentity(
|
||||
id: "copy",
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: theme.list.itemAccentColor)),
|
||||
action: { [weak state] in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
state.copyCredentials(\.url)
|
||||
},
|
||||
animateScale: false
|
||||
)
|
||||
)
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 14.0),
|
||||
isInteractive: true
|
||||
)),
|
||||
action: nil
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
let credentialsKeyTitle = credentialsKeyTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.CreateExternalStream_StreamKey, font: Font.regular(15.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: credentialsAreaSize.width - credentialsSideInset * 2.0, height: credentialsAreaSize.height),
|
||||
transition: context.transition
|
||||
credentialsSectionItems.append(
|
||||
AnyComponentWithIdentity(id: "key", component: AnyComponent(
|
||||
ListActionItemComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: environment.strings.CreateExternalStream_StreamKey,
|
||||
font: Font.regular(15.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: credentials.streamKey, font: Font.regular(17.0), textColor: theme.list.itemAccentColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
truncationType: .middle,
|
||||
maximumNumberOfLines: 1
|
||||
)))
|
||||
], alignment: .left, spacing: 5.0)),
|
||||
contentInsets: UIEdgeInsets(top: 14.0, left: 0.0, bottom: 14.0, right: 0.0),
|
||||
accessory: .custom(ListActionItemComponent.CustomAccessory(
|
||||
component: AnyComponentWithIdentity(
|
||||
id: "copy",
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: theme.list.itemAccentColor)),
|
||||
action: { [weak state] in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
state.copyCredentials(\.streamKey)
|
||||
},
|
||||
animateScale: false
|
||||
)
|
||||
)
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 14.0),
|
||||
isInteractive: true
|
||||
)),
|
||||
action: nil
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
let credentialsURLText = credentialsURLText.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: credentials.url, font: Font.regular(17.0), textColor: theme.list.itemAccentColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
truncationType: .middle,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: credentialsAreaSize.width - credentialsSideInset * 2.0 - 22.0, height: credentialsAreaSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let credentialsKeyText = credentialsKeyText.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: credentials.streamKey, font: Font.regular(17.0), textColor: theme.list.itemAccentColor, paragraphAlignment: .left)),
|
||||
horizontalAlignment: .left,
|
||||
truncationType: .middle,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: credentialsAreaSize.width - credentialsSideInset * 2.0 - 48.0, height: credentialsAreaSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let credentialsBackground = credentialsBackground.update(
|
||||
component: RoundedRectangle(color: theme.list.itemBlocksBackgroundColor, cornerRadius: 26.0),
|
||||
availableSize: credentialsAreaSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let credentialsStripe = credentialsStripe.update(
|
||||
component: Rectangle(color: theme.list.itemPlainSeparatorColor),
|
||||
availableSize: CGSize(width: credentialsAreaSize.width - credentialsSideInset * 2.0, height: UIScreenPixel),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let credentialsCopyURLButton = credentialsCopyURLButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: theme.list.itemAccentColor)),
|
||||
action: { [weak state] in
|
||||
guard let state = state else {
|
||||
return
|
||||
credentialsSectionItems.append(
|
||||
AnyComponentWithIdentity(id: "revoke", component: AnyComponent(
|
||||
ListActionItemComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
title: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.CreateExternalStream_RevokeStreamKey, font: Font.regular(17.0), textColor: theme.list.itemDestructiveColor)),
|
||||
horizontalAlignment: .center,
|
||||
truncationType: .middle,
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
titleAlignment: .center,
|
||||
action: { [weak state] _ in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
let alertController = textAlertController(context: component.context, title: nil, text: environment.strings.CreateExternalStream_Revoke_Text, actions: [TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: environment.strings.CreateExternalStream_Revoke_Revoke, action: { [weak state] in
|
||||
state?.getCredentials(revoke: true)
|
||||
})])
|
||||
environment.controller()?.present(alertController, in: .window(.root))
|
||||
}
|
||||
state.copyCredentials(\.url)
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)),
|
||||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
let credentialsSection = credentialsSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
header: nil,
|
||||
footer: nil,
|
||||
items: credentialsSectionItems
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let credentialsCopyKeyButton = credentialsCopyKeyButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: theme.list.itemAccentColor)),
|
||||
action: { [weak state] in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
state.copyCredentials(\.streamKey)
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)),
|
||||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(credentialsBackground
|
||||
.position(CGPoint(x: credentialsFrame.midX, y: credentialsFrame.midY))
|
||||
)
|
||||
|
||||
context.add(credentialsStripe
|
||||
.position(CGPoint(x: credentialsFrame.minX + credentialsSideInset + credentialsStripe.size.width / 2.0, y: credentialsFrame.minY + credentialsItemHeight))
|
||||
)
|
||||
|
||||
context.add(credentialsURLTitle
|
||||
.position(CGPoint(x: credentialsFrame.minX + credentialsSideInset + credentialsURLTitle.size.width / 2.0, y: credentialsFrame.minY + credentialsTopInset + credentialsURLTitle.size.height / 2.0))
|
||||
)
|
||||
context.add(credentialsURLText
|
||||
.position(CGPoint(x: credentialsFrame.minX + credentialsSideInset + credentialsURLText.size.width / 2.0, y: credentialsFrame.minY + credentialsTopInset + credentialsTitleSpacing + credentialsURLTitle.size.height + credentialsURLText.size.height / 2.0))
|
||||
)
|
||||
context.add(credentialsCopyURLButton
|
||||
.position(CGPoint(x: credentialsFrame.maxX - 4.0 - credentialsCopyURLButton.size.width / 2.0, y: credentialsFrame.minY + credentialsItemHeight / 2.0))
|
||||
)
|
||||
|
||||
context.add(credentialsKeyTitle
|
||||
.position(CGPoint(x: credentialsFrame.minX + credentialsSideInset + credentialsKeyTitle.size.width / 2.0, y: credentialsFrame.minY + credentialsItemHeight + credentialsTopInset + credentialsKeyTitle.size.height / 2.0))
|
||||
)
|
||||
context.add(credentialsKeyText
|
||||
.position(CGPoint(x: credentialsFrame.minX + credentialsSideInset + credentialsKeyText.size.width / 2.0, y: credentialsFrame.minY + credentialsItemHeight + credentialsTopInset + credentialsTitleSpacing + credentialsKeyTitle.size.height + credentialsKeyText.size.height / 2.0))
|
||||
)
|
||||
context.add(credentialsCopyKeyButton
|
||||
.position(CGPoint(x: credentialsFrame.maxX - 4.0 - credentialsCopyKeyButton.size.width / 2.0, y: credentialsFrame.minY + credentialsItemHeight + credentialsItemHeight / 2.0))
|
||||
)
|
||||
context.add(credentialsSection
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: textFrame.maxY + 30.0 + credentialsSection.size.height / 2.0)))
|
||||
} else if !context.state.isDelayingLoadingIndication {
|
||||
let credentialsFrame = CGRect(origin: CGPoint(x: sideInset, y: textFrame.maxY + 30.0), size: credentialsAreaSize)
|
||||
let activityIndicator = activityIndicator.update(
|
||||
component: ActivityIndicatorComponent(color: theme.list.controlSecondaryColor),
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
|
|
|
|||
|
|
@ -548,34 +548,6 @@ private func stringForRight(strings: PresentationStrings, right: TelegramChatAdm
|
|||
}
|
||||
}
|
||||
|
||||
private func rightDependencies(_ right: TelegramChatAdminRightsFlags) -> [TelegramChatAdminRightsFlags] {
|
||||
if right.contains(.canChangeInfo) {
|
||||
return []
|
||||
} else if right.contains(.canPostMessages) {
|
||||
return []
|
||||
} else if right.contains(.canEditMessages) {
|
||||
return []
|
||||
} else if right.contains(.canDeleteMessages) {
|
||||
return []
|
||||
} else if right.contains(.canBanUsers) {
|
||||
return []
|
||||
} else if right.contains(.canInviteUsers) {
|
||||
return []
|
||||
} else if right.contains(.canPinMessages) {
|
||||
return []
|
||||
} else if right.contains(.canAddAdmins) {
|
||||
return []
|
||||
} else if right.contains(.canManageDirect) {
|
||||
return []
|
||||
} else if right.contains(.canManageCalls) {
|
||||
return []
|
||||
} else if right.contains(.canBeAnonymous) {
|
||||
return []
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private func canEditAdminRights(accountPeerId: EnginePeer.Id, channelPeer: EnginePeer, initialParticipant: ChannelParticipant?) -> Bool {
|
||||
if case let .channel(channel) = channelPeer {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
|
|
@ -658,6 +630,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
|||
.direct(.canChangeInfo),
|
||||
.sub(.messages, messageRelatedFlags),
|
||||
.sub(.stories, storiesRelatedFlags),
|
||||
.direct(.canBanUsers),
|
||||
.direct(.canInviteUsers),
|
||||
.direct(.canManageDirect),
|
||||
.direct(.canManageCalls),
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
|||
var price: String = ""
|
||||
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
|
||||
price = "≈\(formatTonUsdValue(sendPaidMessageStars, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
price = "~\(formatTonUsdValue(sendPaidMessageStars, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
|
||||
entries.append(.messagePriceHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePrice))
|
||||
entries.append(.messagePrice(presentationData.theme, sendPaidMessageStars, configuration.paidMessageMaxAmount, price))
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ private func incomingMessagePrivacyScreenEntries(presentationData: PresentationD
|
|||
|
||||
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
|
||||
let price = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
let price = "~\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
|
||||
entries.append(.price(value: amount.value, maxValue: configuration.paidMessageMaxAmount, price: price, isEnabled: isPremium))
|
||||
entries.append(.priceInfo(commission: configuration.paidMessageCommissionPermille / 10, value: price))
|
||||
|
|
|
|||
|
|
@ -180,10 +180,10 @@ final class MonetizationBalanceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
case .ton:
|
||||
let cryptoValue = formatTonAmountText(stats.balances.availableBalance.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
amountString = tonAmountAttributedString(cryptoValue, integralFont: integralFont, fractionalFont: fractionalFont, color: item.presentationData.theme.list.itemPrimaryTextColor, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
|
||||
value = stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
value = stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
case .stars:
|
||||
amountString = NSAttributedString(string: presentationStringsFormattedNumber(stats.balances.availableBalance.amount, item.presentationData.dateTimeFormat.groupingSeparator), font: integralFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
value = stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(stats.balances.availableBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
isStars = true
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, stats.forwards - Int($0))))" } ?? "–",
|
||||
item.publicShares.flatMap { "~\( compactNumericCountString(max(0, stats.forwards - Int($0))))" } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PrivateShares,
|
||||
nil,
|
||||
.generic
|
||||
|
|
@ -485,7 +485,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, views.forwardCount - Int($0))))" } ?? "–",
|
||||
item.publicShares.flatMap { "~\( compactNumericCountString(max(0, views.forwardCount - Int($0))))" } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PrivateShares,
|
||||
nil,
|
||||
.generic
|
||||
|
|
@ -512,7 +512,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"≈\(Int(stats.premiumAudience?.value ?? 0))",
|
||||
"~\(Int(stats.premiumAudience?.value ?? 0))",
|
||||
item.isGroup ? item.presentationData.strings.Stats_Boosts_PremiumMembers : item.presentationData.strings.Stats_Boosts_PremiumSubscribers,
|
||||
(String(format: "%.02f%%", premiumSubscribers * 100.0), .generic),
|
||||
.generic
|
||||
|
|
@ -770,7 +770,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.availableBalance.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Available,
|
||||
(stats.balances.availableBalance.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.availableBalance.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -780,7 +780,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.currentBalance.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Current,
|
||||
(stats.balances.currentBalance.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.currentBalance.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.currentBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -790,7 +790,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.overallRevenue.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Total,
|
||||
(stats.balances.overallRevenue.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.overallRevenue.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -800,7 +800,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(additionalStats.balances.availableBalance.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
" ",
|
||||
(additionalStats.balances.availableBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.availableBalance.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.availableBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(additionalStats.balances.availableBalance.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
@ -810,7 +810,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(additionalStats.balances.currentBalance.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
" ",
|
||||
(additionalStats.balances.currentBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.currentBalance.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.currentBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(additionalStats.balances.currentBalance.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
@ -820,7 +820,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(additionalStats.balances.overallRevenue.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
" ",
|
||||
(additionalStats.balances.overallRevenue.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.overallRevenue.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.overallRevenue.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(additionalStats.balances.overallRevenue.amount.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
@ -834,7 +834,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.availableBalance.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_Overview_Available,
|
||||
(stats.balances.availableBalance.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.availableBalance.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.availableBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -844,7 +844,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.currentBalance.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_Overview_Current,
|
||||
(stats.balances.currentBalance.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.currentBalance.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.currentBalance.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -854,7 +854,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.overallRevenue.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_Overview_Total,
|
||||
(stats.balances.overallRevenue.amount.value == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.overallRevenue.amount.value == 0 ? "" : "~\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
|
|
@ -869,7 +869,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(stats.balances.availableBalance.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Available,
|
||||
(stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.availableBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(stats.balances.availableBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
@ -879,7 +879,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(stats.balances.currentBalance.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Current,
|
||||
(stats.balances.currentBalance.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.currentBalance.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(stats.balances.currentBalance.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
@ -889,7 +889,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
|||
item.presentationData,
|
||||
formatStarsAmountText(stats.balances.overallRevenue.amount, dateTimeFormat: item.presentationData.dateTimeFormat),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Total,
|
||||
(stats.balances.overallRevenue.amount == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.overallRevenue.amount == StarsAmount.zero ? "" : "~\(formatTonUsdValue(stats.balances.overallRevenue.amount.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -620,7 +620,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1189364422] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) }
|
||||
dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) }
|
||||
dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
|
||||
dict[-614898352] = { return Api.MessageAction.parse_messageActionStarGift($0) }
|
||||
dict[-366202413] = { return Api.MessageAction.parse_messageActionStarGift($0) }
|
||||
dict[2000845012] = { return Api.MessageAction.parse_messageActionStarGiftPurchaseOffer($0) }
|
||||
dict[1940760427] = { return Api.MessageAction.parse_messageActionStarGiftPurchaseOfferDeclined($0) }
|
||||
dict[-1787656893] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
|
||||
|
|
@ -915,7 +915,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1681948327] = { return Api.SavedDialog.parse_monoForumDialog($0) }
|
||||
dict[-1115174036] = { return Api.SavedDialog.parse_savedDialog($0) }
|
||||
dict[-881854424] = { return Api.SavedReactionTag.parse_savedReactionTag($0) }
|
||||
dict[-1987861422] = { return Api.SavedStarGift.parse_savedStarGift($0) }
|
||||
dict[-355041186] = { return Api.SavedStarGift.parse_savedStarGift($0) }
|
||||
dict[1040931690] = { return Api.SearchPostsFlood.parse_searchPostsFlood($0) }
|
||||
dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) }
|
||||
dict[2137295719] = { return Api.SearchResultsPosition.parse_searchResultPosition($0) }
|
||||
|
|
@ -980,8 +980,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2109703795] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
|
||||
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
|
||||
dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) }
|
||||
dict[463097215] = { return Api.StarGift.parse_starGift($0) }
|
||||
dict[-1624264554] = { return Api.StarGift.parse_starGiftUnique($0) }
|
||||
dict[1453798502] = { return Api.StarGift.parse_starGift($0) }
|
||||
dict[1453155529] = { return Api.StarGift.parse_starGiftUnique($0) }
|
||||
dict[-753154979] = { return Api.StarGiftActiveAuctionState.parse_starGiftActiveAuctionState($0) }
|
||||
dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
|
||||
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
|
||||
|
|
@ -991,8 +991,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[520210263] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdBackdrop($0) }
|
||||
dict[1219145276] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdModel($0) }
|
||||
dict[1242965043] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdPattern($0) }
|
||||
dict[-1419714037] = { return Api.StarGiftAuctionAcquiredGift.parse_starGiftAuctionAcquiredGift($0) }
|
||||
dict[1571835723] = { return Api.StarGiftAuctionState.parse_starGiftAuctionState($0) }
|
||||
dict[1118831432] = { return Api.StarGiftAuctionAcquiredGift.parse_starGiftAuctionAcquiredGift($0) }
|
||||
dict[984483112] = { return Api.StarGiftAuctionRound.parse_starGiftAuctionRound($0) }
|
||||
dict[178266597] = { return Api.StarGiftAuctionRound.parse_starGiftAuctionRoundExtendable($0) }
|
||||
dict[1998212710] = { return Api.StarGiftAuctionState.parse_starGiftAuctionState($0) }
|
||||
dict[2107014202] = { return Api.StarGiftAuctionState.parse_starGiftAuctionStateFinished($0) }
|
||||
dict[-30197422] = { return Api.StarGiftAuctionState.parse_starGiftAuctionStateNotModified($0) }
|
||||
dict[787403204] = { return Api.StarGiftAuctionUserState.parse_starGiftAuctionUserState($0) }
|
||||
|
|
@ -1493,12 +1495,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1803939105] = { return Api.payments.ResaleStarGifts.parse_resaleStarGifts($0) }
|
||||
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
||||
dict[-1779201615] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) }
|
||||
dict[-1745778728] = { return Api.payments.StarGiftActiveAuctions.parse_starGiftActiveAuctions($0) }
|
||||
dict[-1359565892] = { return Api.payments.StarGiftActiveAuctions.parse_starGiftActiveAuctions($0) }
|
||||
dict[-617358640] = { return Api.payments.StarGiftActiveAuctions.parse_starGiftActiveAuctionsNotModified($0) }
|
||||
dict[2103169520] = { return Api.payments.StarGiftAuctionAcquiredGifts.parse_starGiftAuctionAcquiredGifts($0) }
|
||||
dict[244900980] = { return Api.payments.StarGiftAuctionState.parse_starGiftAuctionState($0) }
|
||||
dict[1798960364] = { return Api.payments.StarGiftAuctionState.parse_starGiftAuctionState($0) }
|
||||
dict[-1977011469] = { return Api.payments.StarGiftCollections.parse_starGiftCollections($0) }
|
||||
dict[-1598402793] = { return Api.payments.StarGiftCollections.parse_starGiftCollectionsNotModified($0) }
|
||||
dict[1187439471] = { return Api.payments.StarGiftUpgradeAttributes.parse_starGiftUpgradeAttributes($0) }
|
||||
dict[1038213101] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) }
|
||||
dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) }
|
||||
dict[785918357] = { return Api.payments.StarGifts.parse_starGifts($0) }
|
||||
|
|
@ -2243,6 +2246,8 @@ public extension Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarGiftAuctionAcquiredGift:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarGiftAuctionRound:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarGiftAuctionState:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarGiftAuctionUserState:
|
||||
|
|
@ -2677,6 +2682,8 @@ public extension Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.payments.StarGiftCollections:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.payments.StarGiftUpgradeAttributes:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.payments.StarGiftUpgradePreview:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.payments.StarGiftWithdrawalUrl:
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,7 @@ public extension Api {
|
|||
case messageActionSetChatTheme(theme: Api.ChatTheme)
|
||||
case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper)
|
||||
case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?)
|
||||
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, prepaidUpgradeHash: String?, giftMsgId: Int32?, toId: Api.Peer?)
|
||||
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, prepaidUpgradeHash: String?, giftMsgId: Int32?, toId: Api.Peer?, giftNum: Int32?)
|
||||
case messageActionStarGiftPurchaseOffer(flags: Int32, gift: Api.StarGift, price: Api.StarsAmount, expiresAt: Int32)
|
||||
case messageActionStarGiftPurchaseOfferDeclined(flags: Int32, gift: Api.StarGift, price: Api.StarsAmount)
|
||||
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleAmount: Api.StarsAmount?, canTransferAt: Int32?, canResellAt: Int32?, dropOriginalDetailsStars: Int64?)
|
||||
|
|
@ -1462,9 +1462,9 @@ public extension Api {
|
|||
serializeInt32(period, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(autoSettingFrom!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId, let toId):
|
||||
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId, let toId, let giftNum):
|
||||
if boxed {
|
||||
buffer.appendInt32(-614898352)
|
||||
buffer.appendInt32(-366202413)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
gift.serialize(buffer, true)
|
||||
|
|
@ -1478,6 +1478,7 @@ public extension Api {
|
|||
if Int(flags) & Int(1 << 14) != 0 {serializeString(prepaidUpgradeHash!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(giftMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 18) != 0 {toId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 19) != 0 {serializeInt32(giftNum!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageActionStarGiftPurchaseOffer(let flags, let gift, let price, let expiresAt):
|
||||
if boxed {
|
||||
|
|
@ -1697,8 +1698,8 @@ public extension Api {
|
|||
return ("messageActionSetChatWallPaper", [("flags", flags as Any), ("wallpaper", wallpaper as Any)])
|
||||
case .messageActionSetMessagesTTL(let flags, let period, let autoSettingFrom):
|
||||
return ("messageActionSetMessagesTTL", [("flags", flags as Any), ("period", period as Any), ("autoSettingFrom", autoSettingFrom as Any)])
|
||||
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId, let toId):
|
||||
return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any), ("giftMsgId", giftMsgId as Any), ("toId", toId as Any)])
|
||||
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId, let toId, let giftNum):
|
||||
return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any), ("giftMsgId", giftMsgId as Any), ("toId", toId as Any), ("giftNum", giftNum as Any)])
|
||||
case .messageActionStarGiftPurchaseOffer(let flags, let gift, let price, let expiresAt):
|
||||
return ("messageActionStarGiftPurchaseOffer", [("flags", flags as Any), ("gift", gift as Any), ("price", price as Any), ("expiresAt", expiresAt as Any)])
|
||||
case .messageActionStarGiftPurchaseOfferDeclined(let flags, let gift, let price):
|
||||
|
|
@ -2477,6 +2478,8 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _13: Int32?
|
||||
if Int(_1!) & Int(1 << 19) != 0 {_13 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
|
|
@ -2489,8 +2492,9 @@ public extension Api {
|
|||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 15) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9, prepaidUpgradeHash: _10, giftMsgId: _11, toId: _12)
|
||||
let _c13 = (Int(_1!) & Int(1 << 19) == 0) || _13 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
|
||||
return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9, prepaidUpgradeHash: _10, giftMsgId: _11, toId: _12, giftNum: _13)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -626,13 +626,13 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum SavedStarGift: TypeConstructorDescription {
|
||||
case savedStarGift(flags: Int32, fromId: Api.Peer?, date: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, msgId: Int32?, savedId: Int64?, convertStars: Int64?, upgradeStars: Int64?, canExportAt: Int32?, transferStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?, collectionId: [Int32]?, prepaidUpgradeHash: String?, dropOriginalDetailsStars: Int64?)
|
||||
case savedStarGift(flags: Int32, fromId: Api.Peer?, date: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, msgId: Int32?, savedId: Int64?, convertStars: Int64?, upgradeStars: Int64?, canExportAt: Int32?, transferStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?, collectionId: [Int32]?, prepaidUpgradeHash: String?, dropOriginalDetailsStars: Int64?, giftNum: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash, let dropOriginalDetailsStars):
|
||||
case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash, let dropOriginalDetailsStars, let giftNum):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1987861422)
|
||||
buffer.appendInt32(-355041186)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {fromId!.serialize(buffer, true)}
|
||||
|
|
@ -654,14 +654,15 @@ public extension Api {
|
|||
}}
|
||||
if Int(flags) & Int(1 << 16) != 0 {serializeString(prepaidUpgradeHash!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 18) != 0 {serializeInt64(dropOriginalDetailsStars!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 19) != 0 {serializeInt32(giftNum!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash, let dropOriginalDetailsStars):
|
||||
return ("savedStarGift", [("flags", flags as Any), ("fromId", fromId as Any), ("date", date as Any), ("gift", gift as Any), ("message", message as Any), ("msgId", msgId as Any), ("savedId", savedId as Any), ("convertStars", convertStars as Any), ("upgradeStars", upgradeStars as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any), ("collectionId", collectionId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any), ("dropOriginalDetailsStars", dropOriginalDetailsStars as Any)])
|
||||
case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash, let dropOriginalDetailsStars, let giftNum):
|
||||
return ("savedStarGift", [("flags", flags as Any), ("fromId", fromId as Any), ("date", date as Any), ("gift", gift as Any), ("message", message as Any), ("msgId", msgId as Any), ("savedId", savedId as Any), ("convertStars", convertStars as Any), ("upgradeStars", upgradeStars as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any), ("collectionId", collectionId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any), ("dropOriginalDetailsStars", dropOriginalDetailsStars as Any), ("giftNum", giftNum as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -706,6 +707,8 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) }
|
||||
var _16: Int64?
|
||||
if Int(_1!) & Int(1 << 18) != 0 {_16 = reader.readInt64() }
|
||||
var _17: Int32?
|
||||
if Int(_1!) & Int(1 << 19) != 0 {_17 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -722,8 +725,9 @@ public extension Api {
|
|||
let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 18) == 0) || _16 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 {
|
||||
return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14, prepaidUpgradeHash: _15, dropOriginalDetailsStars: _16)
|
||||
let _c17 = (Int(_1!) & Int(1 << 19) == 0) || _17 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
|
||||
return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14, prepaidUpgradeHash: _15, dropOriginalDetailsStars: _16, giftNum: _17)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -288,14 +288,14 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum StarGift: TypeConstructorDescription {
|
||||
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?, auctionSlug: String?, giftsPerRound: Int32?)
|
||||
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, themePeer: Api.Peer?, peerColor: Api.PeerColor?, hostId: Api.Peer?, offerMinStars: Int32?)
|
||||
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?, auctionSlug: String?, giftsPerRound: Int32?, auctionStartDate: Int32?, upgradeVariants: Int32?)
|
||||
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, themePeer: Api.Peer?, peerColor: Api.PeerColor?, hostId: Api.Peer?, offerMinStars: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate, let auctionSlug, let giftsPerRound):
|
||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate, let auctionSlug, let giftsPerRound, let auctionStartDate, let upgradeVariants):
|
||||
if boxed {
|
||||
buffer.appendInt32(463097215)
|
||||
buffer.appendInt32(1453798502)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
|
|
@ -316,10 +316,12 @@ public extension Api {
|
|||
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(lockedUntilDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeString(auctionSlug!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(giftsPerRound!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(auctionStartDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(upgradeVariants!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor, let hostId, let offerMinStars):
|
||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let valueUsdAmount, let themePeer, let peerColor, let hostId, let offerMinStars):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1624264554)
|
||||
buffer.appendInt32(1453155529)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
|
|
@ -346,6 +348,7 @@ public extension Api {
|
|||
if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(valueAmount!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 8) != 0 {serializeString(valueCurrency!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(valueUsdAmount!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 10) != 0 {themePeer!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {peerColor!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {hostId!.serialize(buffer, true)}
|
||||
|
|
@ -356,10 +359,10 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate, let auctionSlug, let giftsPerRound):
|
||||
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any), ("auctionSlug", auctionSlug as Any), ("giftsPerRound", giftsPerRound as Any)])
|
||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor, let hostId, let offerMinStars):
|
||||
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("themePeer", themePeer as Any), ("peerColor", peerColor as Any), ("hostId", hostId as Any), ("offerMinStars", offerMinStars as Any)])
|
||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate, let auctionSlug, let giftsPerRound, let auctionStartDate, let upgradeVariants):
|
||||
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any), ("auctionSlug", auctionSlug as Any), ("giftsPerRound", giftsPerRound as Any), ("auctionStartDate", auctionStartDate as Any), ("upgradeVariants", upgradeVariants as Any)])
|
||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let valueUsdAmount, let themePeer, let peerColor, let hostId, let offerMinStars):
|
||||
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("valueUsdAmount", valueUsdAmount as Any), ("themePeer", themePeer as Any), ("peerColor", peerColor as Any), ("hostId", hostId as Any), ("offerMinStars", offerMinStars as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +409,10 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 11) != 0 {_18 = parseString(reader) }
|
||||
var _19: Int32?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_19 = reader.readInt32() }
|
||||
var _20: Int32?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_20 = reader.readInt32() }
|
||||
var _21: Int32?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_21 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -425,8 +432,10 @@ public extension Api {
|
|||
let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 11) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 11) == 0) || _19 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 {
|
||||
return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16, lockedUntilDate: _17, auctionSlug: _18, giftsPerRound: _19)
|
||||
let _c20 = (Int(_1!) & Int(1 << 11) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 12) == 0) || _21 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 {
|
||||
return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16, lockedUntilDate: _17, auctionSlug: _18, giftsPerRound: _19, auctionStartDate: _20, upgradeVariants: _21)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -475,20 +484,22 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt64() }
|
||||
var _17: String?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {_17 = parseString(reader) }
|
||||
var _18: Api.Peer?
|
||||
var _18: Int64?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {_18 = reader.readInt64() }
|
||||
var _19: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() {
|
||||
_18 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
_19 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _19: Api.PeerColor?
|
||||
var _20: Api.PeerColor?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {if let signature = reader.readInt32() {
|
||||
_19 = Api.parse(reader, signature: signature) as? Api.PeerColor
|
||||
_20 = Api.parse(reader, signature: signature) as? Api.PeerColor
|
||||
} }
|
||||
var _20: Api.Peer?
|
||||
var _21: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
|
||||
_20 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
_21 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _21: Int32?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_21 = reader.readInt32() }
|
||||
var _22: Int32?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_22 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -506,12 +517,13 @@ public extension Api {
|
|||
let _c15 = (Int(_1!) & Int(1 << 5) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 8) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 11) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 12) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 13) == 0) || _21 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 {
|
||||
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, themePeer: _18, peerColor: _19, hostId: _20, offerMinStars: _21)
|
||||
let _c18 = (Int(_1!) & Int(1 << 8) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 10) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 11) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 12) == 0) || _21 != nil
|
||||
let _c22 = (Int(_1!) & Int(1 << 13) == 0) || _22 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 {
|
||||
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, valueUsdAmount: _18, themePeer: _19, peerColor: _20, hostId: _21, offerMinStars: _22)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -852,13 +864,13 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum StarGiftAuctionAcquiredGift: TypeConstructorDescription {
|
||||
case starGiftAuctionAcquiredGift(flags: Int32, peer: Api.Peer, date: Int32, bidAmount: Int64, round: Int32, pos: Int32, message: Api.TextWithEntities?)
|
||||
case starGiftAuctionAcquiredGift(flags: Int32, peer: Api.Peer, date: Int32, bidAmount: Int64, round: Int32, pos: Int32, message: Api.TextWithEntities?, giftNum: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftAuctionAcquiredGift(let flags, let peer, let date, let bidAmount, let round, let pos, let message):
|
||||
case .starGiftAuctionAcquiredGift(let flags, let peer, let date, let bidAmount, let round, let pos, let message, let giftNum):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1419714037)
|
||||
buffer.appendInt32(1118831432)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
|
|
@ -867,14 +879,15 @@ public extension Api {
|
|||
serializeInt32(round, buffer: buffer, boxed: false)
|
||||
serializeInt32(pos, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(giftNum!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftAuctionAcquiredGift(let flags, let peer, let date, let bidAmount, let round, let pos, let message):
|
||||
return ("starGiftAuctionAcquiredGift", [("flags", flags as Any), ("peer", peer as Any), ("date", date as Any), ("bidAmount", bidAmount as Any), ("round", round as Any), ("pos", pos as Any), ("message", message as Any)])
|
||||
case .starGiftAuctionAcquiredGift(let flags, let peer, let date, let bidAmount, let round, let pos, let message, let giftNum):
|
||||
return ("starGiftAuctionAcquiredGift", [("flags", flags as Any), ("peer", peer as Any), ("date", date as Any), ("bidAmount", bidAmount as Any), ("round", round as Any), ("pos", pos as Any), ("message", message as Any), ("giftNum", giftNum as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -897,6 +910,8 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||
} }
|
||||
var _8: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_8 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -904,8 +919,81 @@ public extension Api {
|
|||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.StarGiftAuctionAcquiredGift.starGiftAuctionAcquiredGift(flags: _1!, peer: _2!, date: _3!, bidAmount: _4!, round: _5!, pos: _6!, message: _7)
|
||||
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.StarGiftAuctionAcquiredGift.starGiftAuctionAcquiredGift(flags: _1!, peer: _2!, date: _3!, bidAmount: _4!, round: _5!, pos: _6!, message: _7, giftNum: _8)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarGiftAuctionRound: TypeConstructorDescription {
|
||||
case starGiftAuctionRound(num: Int32, duration: Int32)
|
||||
case starGiftAuctionRoundExtendable(num: Int32, duration: Int32, extendTop: Int32, extendWindow: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftAuctionRound(let num, let duration):
|
||||
if boxed {
|
||||
buffer.appendInt32(984483112)
|
||||
}
|
||||
serializeInt32(num, buffer: buffer, boxed: false)
|
||||
serializeInt32(duration, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .starGiftAuctionRoundExtendable(let num, let duration, let extendTop, let extendWindow):
|
||||
if boxed {
|
||||
buffer.appendInt32(178266597)
|
||||
}
|
||||
serializeInt32(num, buffer: buffer, boxed: false)
|
||||
serializeInt32(duration, buffer: buffer, boxed: false)
|
||||
serializeInt32(extendTop, buffer: buffer, boxed: false)
|
||||
serializeInt32(extendWindow, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftAuctionRound(let num, let duration):
|
||||
return ("starGiftAuctionRound", [("num", num as Any), ("duration", duration as Any)])
|
||||
case .starGiftAuctionRoundExtendable(let num, let duration, let extendTop, let extendWindow):
|
||||
return ("starGiftAuctionRoundExtendable", [("num", num as Any), ("duration", duration as Any), ("extendTop", extendTop as Any), ("extendWindow", extendWindow as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starGiftAuctionRound(_ reader: BufferReader) -> StarGiftAuctionRound? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StarGiftAuctionRound.starGiftAuctionRound(num: _1!, duration: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_starGiftAuctionRoundExtendable(_ reader: BufferReader) -> StarGiftAuctionRound? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StarGiftAuctionRound.starGiftAuctionRoundExtendable(num: _1!, duration: _2!, extendTop: _3!, extendWindow: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -916,15 +1004,15 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum StarGiftAuctionState: TypeConstructorDescription {
|
||||
case starGiftAuctionState(version: Int32, startDate: Int32, endDate: Int32, minBidAmount: Int64, bidLevels: [Api.AuctionBidLevel], topBidders: [Int64], nextRoundAt: Int32, giftsLeft: Int32, currentRound: Int32, totalRounds: Int32)
|
||||
case starGiftAuctionState(version: Int32, startDate: Int32, endDate: Int32, minBidAmount: Int64, bidLevels: [Api.AuctionBidLevel], topBidders: [Int64], nextRoundAt: Int32, lastGiftNum: Int32, giftsLeft: Int32, currentRound: Int32, totalRounds: Int32, rounds: [Api.StarGiftAuctionRound])
|
||||
case starGiftAuctionStateFinished(startDate: Int32, endDate: Int32, averagePrice: Int64)
|
||||
case starGiftAuctionStateNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftAuctionState(let version, let startDate, let endDate, let minBidAmount, let bidLevels, let topBidders, let nextRoundAt, let giftsLeft, let currentRound, let totalRounds):
|
||||
case .starGiftAuctionState(let version, let startDate, let endDate, let minBidAmount, let bidLevels, let topBidders, let nextRoundAt, let lastGiftNum, let giftsLeft, let currentRound, let totalRounds, let rounds):
|
||||
if boxed {
|
||||
buffer.appendInt32(1571835723)
|
||||
buffer.appendInt32(1998212710)
|
||||
}
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
serializeInt32(startDate, buffer: buffer, boxed: false)
|
||||
|
|
@ -941,9 +1029,15 @@ public extension Api {
|
|||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
serializeInt32(nextRoundAt, buffer: buffer, boxed: false)
|
||||
serializeInt32(lastGiftNum, buffer: buffer, boxed: false)
|
||||
serializeInt32(giftsLeft, buffer: buffer, boxed: false)
|
||||
serializeInt32(currentRound, buffer: buffer, boxed: false)
|
||||
serializeInt32(totalRounds, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(rounds.count))
|
||||
for item in rounds {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .starGiftAuctionStateFinished(let startDate, let endDate, let averagePrice):
|
||||
if boxed {
|
||||
|
|
@ -964,8 +1058,8 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftAuctionState(let version, let startDate, let endDate, let minBidAmount, let bidLevels, let topBidders, let nextRoundAt, let giftsLeft, let currentRound, let totalRounds):
|
||||
return ("starGiftAuctionState", [("version", version as Any), ("startDate", startDate as Any), ("endDate", endDate as Any), ("minBidAmount", minBidAmount as Any), ("bidLevels", bidLevels as Any), ("topBidders", topBidders as Any), ("nextRoundAt", nextRoundAt as Any), ("giftsLeft", giftsLeft as Any), ("currentRound", currentRound as Any), ("totalRounds", totalRounds as Any)])
|
||||
case .starGiftAuctionState(let version, let startDate, let endDate, let minBidAmount, let bidLevels, let topBidders, let nextRoundAt, let lastGiftNum, let giftsLeft, let currentRound, let totalRounds, let rounds):
|
||||
return ("starGiftAuctionState", [("version", version as Any), ("startDate", startDate as Any), ("endDate", endDate as Any), ("minBidAmount", minBidAmount as Any), ("bidLevels", bidLevels as Any), ("topBidders", topBidders as Any), ("nextRoundAt", nextRoundAt as Any), ("lastGiftNum", lastGiftNum as Any), ("giftsLeft", giftsLeft as Any), ("currentRound", currentRound as Any), ("totalRounds", totalRounds as Any), ("rounds", rounds as Any)])
|
||||
case .starGiftAuctionStateFinished(let startDate, let endDate, let averagePrice):
|
||||
return ("starGiftAuctionStateFinished", [("startDate", startDate as Any), ("endDate", endDate as Any), ("averagePrice", averagePrice as Any)])
|
||||
case .starGiftAuctionStateNotModified:
|
||||
|
|
@ -998,6 +1092,12 @@ public extension Api {
|
|||
_9 = reader.readInt32()
|
||||
var _10: Int32?
|
||||
_10 = reader.readInt32()
|
||||
var _11: Int32?
|
||||
_11 = reader.readInt32()
|
||||
var _12: [Api.StarGiftAuctionRound]?
|
||||
if let _ = reader.readInt32() {
|
||||
_12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAuctionRound.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -1008,8 +1108,10 @@ public extension Api {
|
|||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.StarGiftAuctionState.starGiftAuctionState(version: _1!, startDate: _2!, endDate: _3!, minBidAmount: _4!, bidLevels: _5!, topBidders: _6!, nextRoundAt: _7!, giftsLeft: _8!, currentRound: _9!, totalRounds: _10!)
|
||||
let _c11 = _11 != nil
|
||||
let _c12 = _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.StarGiftAuctionState.starGiftAuctionState(version: _1!, startDate: _2!, endDate: _3!, minBidAmount: _4!, bidLevels: _5!, topBidders: _6!, nextRoundAt: _7!, lastGiftNum: _8!, giftsLeft: _9!, currentRound: _10!, totalRounds: _11!, rounds: _12!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -1526,135 +1628,3 @@ public extension Api {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsRevenueStatus: TypeConstructorDescription {
|
||||
case starsRevenueStatus(flags: Int32, currentBalance: Api.StarsAmount, availableBalance: Api.StarsAmount, overallRevenue: Api.StarsAmount, nextWithdrawalAt: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt):
|
||||
if boxed {
|
||||
buffer.appendInt32(-21080943)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
currentBalance.serialize(buffer, true)
|
||||
availableBalance.serialize(buffer, true)
|
||||
overallRevenue.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(nextWithdrawalAt!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt):
|
||||
return ("starsRevenueStatus", [("flags", flags as Any), ("currentBalance", currentBalance as Any), ("availableBalance", availableBalance as Any), ("overallRevenue", overallRevenue as Any), ("nextWithdrawalAt", nextWithdrawalAt as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsRevenueStatus(_ reader: BufferReader) -> StarsRevenueStatus? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _3: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _4: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!, nextWithdrawalAt: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsSubscription: TypeConstructorDescription {
|
||||
case starsSubscription(flags: Int32, id: String, peer: Api.Peer, untilDate: Int32, pricing: Api.StarsSubscriptionPricing, chatInviteHash: String?, title: String?, photo: Api.WebDocument?, invoiceSlug: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsSubscription(let flags, let id, let peer, let untilDate, let pricing, let chatInviteHash, let title, let photo, let invoiceSlug):
|
||||
if boxed {
|
||||
buffer.appendInt32(779004698)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
||||
pricing.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeString(chatInviteHash!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {serializeString(invoiceSlug!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsSubscription(let flags, let id, let peer, let untilDate, let pricing, let chatInviteHash, let title, let photo, let invoiceSlug):
|
||||
return ("starsSubscription", [("flags", flags as Any), ("id", id as Any), ("peer", peer as Any), ("untilDate", untilDate as Any), ("pricing", pricing as Any), ("chatInviteHash", chatInviteHash as Any), ("title", title as Any), ("photo", photo as Any), ("invoiceSlug", invoiceSlug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsSubscription(_ reader: BufferReader) -> StarsSubscription? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Api.StarsSubscriptionPricing?
|
||||
if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
|
||||
var _8: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _9: String?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {_9 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.StarsSubscription.starsSubscription(flags: _1!, id: _2!, peer: _3!, untilDate: _4!, pricing: _5!, chatInviteHash: _6, title: _7, photo: _8, invoiceSlug: _9)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,135 @@
|
|||
public extension Api {
|
||||
enum StarsRevenueStatus: TypeConstructorDescription {
|
||||
case starsRevenueStatus(flags: Int32, currentBalance: Api.StarsAmount, availableBalance: Api.StarsAmount, overallRevenue: Api.StarsAmount, nextWithdrawalAt: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt):
|
||||
if boxed {
|
||||
buffer.appendInt32(-21080943)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
currentBalance.serialize(buffer, true)
|
||||
availableBalance.serialize(buffer, true)
|
||||
overallRevenue.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(nextWithdrawalAt!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt):
|
||||
return ("starsRevenueStatus", [("flags", flags as Any), ("currentBalance", currentBalance as Any), ("availableBalance", availableBalance as Any), ("overallRevenue", overallRevenue as Any), ("nextWithdrawalAt", nextWithdrawalAt as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsRevenueStatus(_ reader: BufferReader) -> StarsRevenueStatus? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _3: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _4: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!, nextWithdrawalAt: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsSubscription: TypeConstructorDescription {
|
||||
case starsSubscription(flags: Int32, id: String, peer: Api.Peer, untilDate: Int32, pricing: Api.StarsSubscriptionPricing, chatInviteHash: String?, title: String?, photo: Api.WebDocument?, invoiceSlug: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsSubscription(let flags, let id, let peer, let untilDate, let pricing, let chatInviteHash, let title, let photo, let invoiceSlug):
|
||||
if boxed {
|
||||
buffer.appendInt32(779004698)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
||||
pricing.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeString(chatInviteHash!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {serializeString(invoiceSlug!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsSubscription(let flags, let id, let peer, let untilDate, let pricing, let chatInviteHash, let title, let photo, let invoiceSlug):
|
||||
return ("starsSubscription", [("flags", flags as Any), ("id", id as Any), ("peer", peer as Any), ("untilDate", untilDate as Any), ("pricing", pricing as Any), ("chatInviteHash", chatInviteHash as Any), ("title", title as Any), ("photo", photo as Any), ("invoiceSlug", invoiceSlug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsSubscription(_ reader: BufferReader) -> StarsSubscription? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Api.StarsSubscriptionPricing?
|
||||
if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
|
||||
var _8: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _9: String?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {_9 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.StarsSubscription.starsSubscription(flags: _1!, id: _2!, peer: _3!, untilDate: _4!, pricing: _5!, chatInviteHash: _6, title: _7, photo: _8, invoiceSlug: _9)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsSubscriptionPricing: TypeConstructorDescription {
|
||||
case starsSubscriptionPricing(period: Int32, amount: Int64)
|
||||
|
|
@ -1160,227 +1292,3 @@ public extension Api {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StoryFwdHeader: TypeConstructorDescription {
|
||||
case storyFwdHeader(flags: Int32, from: Api.Peer?, fromName: String?, storyId: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyFwdHeader(let flags, let from, let fromName, let storyId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1205411504)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {from!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(fromName!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(storyId!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyFwdHeader(let flags, let from, let fromName, let storyId):
|
||||
return ("storyFwdHeader", [("flags", flags as Any), ("from", from as Any), ("fromName", fromName as Any), ("storyId", storyId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyFwdHeader(_ reader: BufferReader) -> StoryFwdHeader? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StoryFwdHeader.storyFwdHeader(flags: _1!, from: _2, fromName: _3, storyId: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryItem: TypeConstructorDescription {
|
||||
case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?, albums: [Int32]?)
|
||||
case storyItemDeleted(id: Int32)
|
||||
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
|
||||
if boxed {
|
||||
buffer.appendInt32(-302947087)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 18) != 0 {fromId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
media.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 14) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(mediaAreas!.count))
|
||||
for item in mediaAreas! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(privacy!.count))
|
||||
for item in privacy! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(albums!.count))
|
||||
for item in albums! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
break
|
||||
case .storyItemDeleted(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(1374088783)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||
if boxed {
|
||||
buffer.appendInt32(-5388013)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
|
||||
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any), ("albums", albums as Any)])
|
||||
case .storyItemDeleted(let id):
|
||||
return ("storyItemDeleted", [("id", id as Any)])
|
||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||
return ("storyItemSkipped", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyItem(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _5: Api.StoryFwdHeader?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StoryFwdHeader
|
||||
} }
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _9: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
}
|
||||
var _10: [Api.MediaArea]?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let _ = reader.readInt32() {
|
||||
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self)
|
||||
} }
|
||||
var _11: [Api.PrivacyRule]?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
|
||||
} }
|
||||
var _12: Api.StoryViews?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.StoryViews
|
||||
} }
|
||||
var _13: Api.Reaction?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
} }
|
||||
var _14: [Int32]?
|
||||
if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 18) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 17) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 19) == 0) || _14 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
|
||||
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StoryItem.storyItemDeleted(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyItemSkipped(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,227 @@
|
|||
public extension Api {
|
||||
enum StoryFwdHeader: TypeConstructorDescription {
|
||||
case storyFwdHeader(flags: Int32, from: Api.Peer?, fromName: String?, storyId: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyFwdHeader(let flags, let from, let fromName, let storyId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1205411504)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {from!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(fromName!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(storyId!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyFwdHeader(let flags, let from, let fromName, let storyId):
|
||||
return ("storyFwdHeader", [("flags", flags as Any), ("from", from as Any), ("fromName", fromName as Any), ("storyId", storyId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyFwdHeader(_ reader: BufferReader) -> StoryFwdHeader? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StoryFwdHeader.storyFwdHeader(flags: _1!, from: _2, fromName: _3, storyId: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryItem: TypeConstructorDescription {
|
||||
case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?, albums: [Int32]?)
|
||||
case storyItemDeleted(id: Int32)
|
||||
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
|
||||
if boxed {
|
||||
buffer.appendInt32(-302947087)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 18) != 0 {fromId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
media.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 14) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(mediaAreas!.count))
|
||||
for item in mediaAreas! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(privacy!.count))
|
||||
for item in privacy! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(albums!.count))
|
||||
for item in albums! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
break
|
||||
case .storyItemDeleted(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(1374088783)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||
if boxed {
|
||||
buffer.appendInt32(-5388013)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
|
||||
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any), ("albums", albums as Any)])
|
||||
case .storyItemDeleted(let id):
|
||||
return ("storyItemDeleted", [("id", id as Any)])
|
||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||
return ("storyItemSkipped", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyItem(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _5: Api.StoryFwdHeader?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StoryFwdHeader
|
||||
} }
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _9: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
}
|
||||
var _10: [Api.MediaArea]?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let _ = reader.readInt32() {
|
||||
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self)
|
||||
} }
|
||||
var _11: [Api.PrivacyRule]?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
|
||||
} }
|
||||
var _12: Api.StoryViews?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.StoryViews
|
||||
} }
|
||||
var _13: Api.Reaction?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
} }
|
||||
var _14: [Int32]?
|
||||
if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 18) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 17) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 19) == 0) || _14 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
|
||||
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StoryItem.storyItemDeleted(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyItemSkipped(_ reader: BufferReader) -> StoryItem? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryReaction: TypeConstructorDescription {
|
||||
case storyReaction(peerId: Api.Peer, date: Int32, reaction: Api.Reaction)
|
||||
|
|
|
|||
|
|
@ -278,14 +278,14 @@ public extension Api.payments {
|
|||
}
|
||||
public extension Api.payments {
|
||||
enum StarGiftActiveAuctions: TypeConstructorDescription {
|
||||
case starGiftActiveAuctions(auctions: [Api.StarGiftActiveAuctionState], users: [Api.User])
|
||||
case starGiftActiveAuctions(auctions: [Api.StarGiftActiveAuctionState], users: [Api.User], chats: [Api.Chat])
|
||||
case starGiftActiveAuctionsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftActiveAuctions(let auctions, let users):
|
||||
case .starGiftActiveAuctions(let auctions, let users, let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1745778728)
|
||||
buffer.appendInt32(-1359565892)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(auctions.count))
|
||||
|
|
@ -297,6 +297,11 @@ public extension Api.payments {
|
|||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .starGiftActiveAuctionsNotModified:
|
||||
if boxed {
|
||||
|
|
@ -309,8 +314,8 @@ public extension Api.payments {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftActiveAuctions(let auctions, let users):
|
||||
return ("starGiftActiveAuctions", [("auctions", auctions as Any), ("users", users as Any)])
|
||||
case .starGiftActiveAuctions(let auctions, let users, let chats):
|
||||
return ("starGiftActiveAuctions", [("auctions", auctions as Any), ("users", users as Any), ("chats", chats as Any)])
|
||||
case .starGiftActiveAuctionsNotModified:
|
||||
return ("starGiftActiveAuctionsNotModified", [])
|
||||
}
|
||||
|
|
@ -325,10 +330,15 @@ public extension Api.payments {
|
|||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _3: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.payments.StarGiftActiveAuctions.starGiftActiveAuctions(auctions: _1!, users: _2!)
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.payments.StarGiftActiveAuctions.starGiftActiveAuctions(auctions: _1!, users: _2!, chats: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -404,13 +414,13 @@ public extension Api.payments {
|
|||
}
|
||||
public extension Api.payments {
|
||||
enum StarGiftAuctionState: TypeConstructorDescription {
|
||||
case starGiftAuctionState(gift: Api.StarGift, state: Api.StarGiftAuctionState, userState: Api.StarGiftAuctionUserState, timeout: Int32, users: [Api.User])
|
||||
case starGiftAuctionState(gift: Api.StarGift, state: Api.StarGiftAuctionState, userState: Api.StarGiftAuctionUserState, timeout: Int32, users: [Api.User], chats: [Api.Chat])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftAuctionState(let gift, let state, let userState, let timeout, let users):
|
||||
case .starGiftAuctionState(let gift, let state, let userState, let timeout, let users, let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(244900980)
|
||||
buffer.appendInt32(1798960364)
|
||||
}
|
||||
gift.serialize(buffer, true)
|
||||
state.serialize(buffer, true)
|
||||
|
|
@ -421,14 +431,19 @@ public extension Api.payments {
|
|||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftAuctionState(let gift, let state, let userState, let timeout, let users):
|
||||
return ("starGiftAuctionState", [("gift", gift as Any), ("state", state as Any), ("userState", userState as Any), ("timeout", timeout as Any), ("users", users as Any)])
|
||||
case .starGiftAuctionState(let gift, let state, let userState, let timeout, let users, let chats):
|
||||
return ("starGiftAuctionState", [("gift", gift as Any), ("state", state as Any), ("userState", userState as Any), ("timeout", timeout as Any), ("users", users as Any), ("chats", chats as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -451,13 +466,18 @@ public extension Api.payments {
|
|||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _6: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.payments.StarGiftAuctionState.starGiftAuctionState(gift: _1!, state: _2!, userState: _3!, timeout: _4!, users: _5!)
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.payments.StarGiftAuctionState.starGiftAuctionState(gift: _1!, state: _2!, userState: _3!, timeout: _4!, users: _5!, chats: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -520,6 +540,48 @@ public extension Api.payments {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum StarGiftUpgradeAttributes: TypeConstructorDescription {
|
||||
case starGiftUpgradeAttributes(attributes: [Api.StarGiftAttribute])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starGiftUpgradeAttributes(let attributes):
|
||||
if boxed {
|
||||
buffer.appendInt32(1187439471)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(attributes.count))
|
||||
for item in attributes {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starGiftUpgradeAttributes(let attributes):
|
||||
return ("starGiftUpgradeAttributes", [("attributes", attributes as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starGiftUpgradeAttributes(_ reader: BufferReader) -> StarGiftUpgradeAttributes? {
|
||||
var _1: [Api.StarGiftAttribute]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.payments.StarGiftUpgradeAttributes.starGiftUpgradeAttributes(attributes: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum StarGiftUpgradePreview: TypeConstructorDescription {
|
||||
case starGiftUpgradePreview(sampleAttributes: [Api.StarGiftAttribute], prices: [Api.StarGiftUpgradePrice], nextPrices: [Api.StarGiftUpgradePrice])
|
||||
|
|
@ -1616,51 +1678,3 @@ public extension Api.phone {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.photos {
|
||||
enum Photo: TypeConstructorDescription {
|
||||
case photo(photo: Api.Photo, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .photo(let photo, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(539045032)
|
||||
}
|
||||
photo.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .photo(let photo, let users):
|
||||
return ("photo", [("photo", photo as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_photo(_ reader: BufferReader) -> Photo? {
|
||||
var _1: Api.Photo?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Photo
|
||||
}
|
||||
var _2: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.photos.Photo.photo(photo: _1!, users: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,51 @@
|
|||
public extension Api.photos {
|
||||
enum Photo: TypeConstructorDescription {
|
||||
case photo(photo: Api.Photo, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .photo(let photo, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(539045032)
|
||||
}
|
||||
photo.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .photo(let photo, let users):
|
||||
return ("photo", [("photo", photo as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_photo(_ reader: BufferReader) -> Photo? {
|
||||
var _1: Api.Photo?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Photo
|
||||
}
|
||||
var _2: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.photos.Photo.photo(photo: _1!, users: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.photos {
|
||||
enum Photos: TypeConstructorDescription {
|
||||
case photos(photos: [Api.Photo], users: [Api.User])
|
||||
|
|
@ -1474,77 +1522,3 @@ public extension Api.stories {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum StoryReactionsList: TypeConstructorDescription {
|
||||
case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1436583780)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
return ("storyReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyReactionsList(_ reader: BufferReader) -> StoryReactionsList? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.StoryReaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryReaction.self)
|
||||
}
|
||||
var _4: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _5: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,77 @@
|
|||
public extension Api.stories {
|
||||
enum StoryReactionsList: TypeConstructorDescription {
|
||||
case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1436583780)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
return ("storyReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyReactionsList(_ reader: BufferReader) -> StoryReactionsList? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.StoryReaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryReaction.self)
|
||||
}
|
||||
var _4: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _5: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum StoryViews: TypeConstructorDescription {
|
||||
case storyViews(views: [Api.StoryViews], users: [Api.User])
|
||||
|
|
|
|||
|
|
@ -9911,6 +9911,21 @@ public extension Api.functions.payments {
|
|||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func getStarGiftUpgradeAttributes(giftId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarGiftUpgradeAttributes>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1828948824)
|
||||
serializeInt64(giftId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "payments.getStarGiftUpgradeAttributes", parameters: [("giftId", String(describing: giftId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarGiftUpgradeAttributes? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.payments.StarGiftUpgradeAttributes?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.payments.StarGiftUpgradeAttributes
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func getStarGiftUpgradePreview(giftId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarGiftUpgradePreview>) {
|
||||
let buffer = Buffer()
|
||||
|
|
|
|||
|
|
@ -103,8 +103,9 @@ final class GiftAuctionAccessoryPanel: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
let titleText: String = self.strings.ChatList_Auctions_ActiveAuction(Int32(self.states.count))
|
||||
titleItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: titleText, font: titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor))))))
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
var titleText: String = self.strings.ChatList_Auctions_ActiveAuction(Int32(self.states.count))
|
||||
|
||||
let subtitleText: String
|
||||
var subtitleTextColor = self.theme.rootController.navigationBar.secondaryTextColor
|
||||
|
|
@ -134,13 +135,20 @@ final class GiftAuctionAccessoryPanel: ASDisplayNode {
|
|||
subtitleText = self.strings.ChatList_Auctions_Status_Single_Winning(placeText).string
|
||||
}
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
var startTime = currentTime
|
||||
var endTime = currentTime
|
||||
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, _, _) = auctionState.auctionState {
|
||||
if case let .ongoing(_, startDate, _, _, _, _, nextRoundDate, _, _, _, _, _) = auctionState.auctionState {
|
||||
startTime = startDate
|
||||
endTime = nextRoundDate
|
||||
}
|
||||
|
||||
let endTimeout = max(0, endTime - currentTime)
|
||||
let endTimeout: Int32
|
||||
if currentTime < startTime {
|
||||
endTimeout = max(0, startTime - currentTime)
|
||||
titleText = self.strings.ChatList_Auctions_UpcomingAuction
|
||||
} else {
|
||||
endTimeout = max(0, endTime - currentTime)
|
||||
}
|
||||
|
||||
let hours = Int(endTimeout / 3600)
|
||||
let minutes = Int((endTimeout % 3600) / 60)
|
||||
|
|
@ -179,6 +187,8 @@ final class GiftAuctionAccessoryPanel: ASDisplayNode {
|
|||
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "view", content: .text(self.strings.ChatList_Auctions_View)))
|
||||
}
|
||||
|
||||
titleItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: titleText, font: titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor))))))
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
transition: .spring(duration: 0.2),
|
||||
component: AnyComponent(
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||
return TelegramMediaAction(action: .paymentRefunded(peerId: peer.peerId, currency: currency, totalAmount: totalAmount, payload: payload?.makeData(), transactionId: transactionId))
|
||||
case let .messageActionPrizeStars(flags, stars, transactionId, boostPeer, giveawayMsgId):
|
||||
return TelegramMediaAction(action: .prizeStars(amount: stars, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer.peerId, transactionId: transactionId, giveawayMessageId: MessageId(peerId: boostPeer.peerId, namespace: Namespaces.Message.Cloud, id: giveawayMsgId)))
|
||||
case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeMessageId, upgradeStars, fromId, peer, savedId, prepaidUpgradeHash, giftMessageId, toId):
|
||||
case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeMessageId, upgradeStars, fromId, peer, savedId, prepaidUpgradeHash, giftMessageId, toId, number):
|
||||
let text: String?
|
||||
let entities: [MessageTextEntity]?
|
||||
switch message {
|
||||
|
|
@ -195,7 +195,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
}
|
||||
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, isPrepaidUpgrade: (flags & (1 << 13)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, prepaidUpgradeHash: prepaidUpgradeHash, giftMessageId: giftMessageId, upgradeSeparate: (flags & (1 << 16)) != 0, isAuctionAcquired: (flags & (1 << 17)) != 0, toPeerId: toId?.peerId))
|
||||
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, isPrepaidUpgrade: (flags & (1 << 13)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, prepaidUpgradeHash: prepaidUpgradeHash, giftMessageId: giftMessageId, upgradeSeparate: (flags & (1 << 16)) != 0, isAuctionAcquired: (flags & (1 << 17)) != 0, toPeerId: toId?.peerId, number: number))
|
||||
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleAmount, canTransferDate, canResaleDate, dropOriginalDetailsStars):
|
||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne
|
|||
releasedBy: nil,
|
||||
valueAmount: nil,
|
||||
valueCurrency: nil,
|
||||
valueUsdAmount: nil,
|
||||
flags: [],
|
||||
themePeerId: nil,
|
||||
peerColor: nil,
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ public struct Namespaces {
|
|||
public static let cachedProfileSavedMusic: Int8 = 49
|
||||
public static let cachedChatThemes: Int8 = 50
|
||||
public static let cachedLiveStorySendAsPeers: Int8 = 51
|
||||
public static let cachedGiftUpgradesAttributes: Int8 = 52
|
||||
}
|
||||
|
||||
public struct UnorderedItemList {
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String)
|
||||
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
||||
case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?)
|
||||
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, prepaidUpgradeHash: String?, giftMessageId: Int32?, upgradeSeparate: Bool, isAuctionAcquired: Bool, toPeerId: EnginePeer.Id?)
|
||||
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, prepaidUpgradeHash: String?, giftMessageId: Int32?, upgradeSeparate: Bool, isAuctionAcquired: Bool, toPeerId: EnginePeer.Id?, number: Int32?)
|
||||
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?, dropOriginalDetailsStars: Int64?, assigned: Bool, fromOffer: Bool)
|
||||
case paidMessagesRefunded(count: Int32, stars: Int64)
|
||||
case paidMessagesPriceEdited(stars: Int64, broadcastMessagesAllowed: Bool)
|
||||
|
|
@ -396,7 +396,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
}
|
||||
self = .prizeStars(amount: decoder.decodeInt64ForKey("amount", orElse: 0), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: boostPeerId, transactionId: decoder.decodeOptionalStringForKey("transactionId"), giveawayMessageId: giveawayMessageId)
|
||||
case 44:
|
||||
self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), prepaidUpgradeHash: decoder.decodeOptionalStringForKey("prepaidUpgradeHash"), giftMessageId: decoder.decodeOptionalInt32ForKey("giftMessageId"), upgradeSeparate: decoder.decodeOptionalBoolForKey("upgradeSeparate") ?? false, isAuctionAcquired: decoder.decodeOptionalBoolForKey("isAuctionAcquired") ?? false, toPeerId: decoder.decodeOptionalInt64ForKey("toPeerId").flatMap { EnginePeer.Id($0) })
|
||||
self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), prepaidUpgradeHash: decoder.decodeOptionalStringForKey("prepaidUpgradeHash"), giftMessageId: decoder.decodeOptionalInt32ForKey("giftMessageId"), upgradeSeparate: decoder.decodeOptionalBoolForKey("upgradeSeparate") ?? false, isAuctionAcquired: decoder.decodeOptionalBoolForKey("isAuctionAcquired") ?? false, toPeerId: decoder.decodeOptionalInt64ForKey("toPeerId").flatMap { EnginePeer.Id($0) }, number: decoder.decodeOptionalInt32ForKey("number"))
|
||||
case 45:
|
||||
var resaleAmount: CurrencyAmount?
|
||||
if let amount = decoder.decodeCodable(CurrencyAmount.self, forKey: "resaleAmount") {
|
||||
|
|
@ -734,7 +734,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: "giveawayMsgId")
|
||||
}
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate, isAuctionAcquired, toPeerId):
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate, isAuctionAcquired, toPeerId, number):
|
||||
encoder.encodeInt32(44, forKey: "_rawValue")
|
||||
encoder.encodeObject(gift, forKey: "gift")
|
||||
if let convertStars {
|
||||
|
|
@ -798,6 +798,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: "toPeerId")
|
||||
}
|
||||
if let number {
|
||||
encoder.encodeInt32(number, forKey: "number")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "number")
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate, dropOriginalDetailsStars, assigned, fromOffer):
|
||||
encoder.encodeInt32(45, forKey: "_rawValue")
|
||||
encoder.encodeObject(gift, forKey: "gift")
|
||||
|
|
@ -945,7 +950,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
return [peerId]
|
||||
case let .prizeStars(_, _, boostPeerId, _, _):
|
||||
return boostPeerId.flatMap { [$0] } ?? []
|
||||
case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _, _, toPeerId):
|
||||
case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _, _, toPeerId, _):
|
||||
var peerIds: [PeerId] = []
|
||||
if let peerId {
|
||||
peerIds.append(peerId)
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ public class TranslationMessageAttribute: MessageAttribute, Equatable {
|
|||
public let entities: [MessageTextEntity]
|
||||
public let toLang: String
|
||||
|
||||
public let additional:[Additional]
|
||||
public let additional: [Additional]
|
||||
public let pollSolution: Additional?
|
||||
|
||||
|
||||
public var associatedPeerIds: [PeerId] {
|
||||
return []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ public extension TelegramEngine {
|
|||
}
|
||||
|
||||
public func translate(texts: [(String, [MessageTextEntity])], toLang: String) -> Signal<[(String, [MessageTextEntity])], TranslationError> {
|
||||
return _internal_translate_texts(network: self.account.network, texts: texts, toLang: toLang)
|
||||
return _internal_translateTexts(network: self.account.network, texts: texts, toLang: toLang)
|
||||
}
|
||||
|
||||
public func translateMessages(messageIds: [EngineMessage.Id], fromLang: String?, toLang: String, enableLocalIfPossible: Bool) -> Signal<Never, TranslationError> {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ func _internal_translate(network: Network, text: String, toLang: String, entitie
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_translate_texts(network: Network, texts: [(String, [MessageTextEntity])], toLang: String) -> Signal<[(String, [MessageTextEntity])], TranslationError> {
|
||||
func _internal_translateTexts(network: Network, texts: [(String, [MessageTextEntity])], toLang: String) -> Signal<[(String, [MessageTextEntity])], TranslationError> {
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 1)
|
||||
|
||||
|
|
@ -112,9 +112,9 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
return .never()
|
||||
}
|
||||
|
||||
let polls = messages.compactMap { msg in
|
||||
if let poll = msg.media.first as? TelegramMediaPoll {
|
||||
return (poll, msg.id)
|
||||
let polls = messages.compactMap { message in
|
||||
if let poll = message.media.first as? TelegramMediaPoll {
|
||||
return (poll, message.id)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -128,9 +128,19 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
if let solution = poll.results.solution {
|
||||
texts.append((solution.text, solution.entities))
|
||||
}
|
||||
return _internal_translate_texts(network: account.network, texts: texts, toLang: toLang)
|
||||
return _internal_translateTexts(network: account.network, texts: texts, toLang: toLang)
|
||||
}
|
||||
|
||||
let audioTranscriptions = messages.compactMap { message in
|
||||
if let audioTranscription = message.attributes.first(where: { $0 is AudioTranscriptionMessageAttribute }) as? AudioTranscriptionMessageAttribute, !audioTranscription.text.isEmpty && !audioTranscription.isPending {
|
||||
return (audioTranscription.text, message.id)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
let audioTranscriptionsSignals = audioTranscriptions.map { (text, id) in
|
||||
return _internal_translate(network: account.network, text: text, toLang: toLang)
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 0)
|
||||
|
|
@ -197,8 +207,8 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
}
|
||||
}
|
||||
|
||||
return combineLatest(msgs, combineLatest(pollSignals))
|
||||
|> mapToSignal { (result, pollResults) -> Signal<Void, TranslationError> in
|
||||
return combineLatest(msgs, combineLatest(pollSignals), combineLatest(audioTranscriptionsSignals))
|
||||
|> mapToSignal { (result, pollResults, audioTranscriptionsResults) -> Signal<Void, TranslationError> in
|
||||
return account.postbox.transaction { transaction in
|
||||
if case let .translateResult(results) = result {
|
||||
var index = 0
|
||||
|
|
@ -218,6 +228,7 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
if !pollResults.isEmpty {
|
||||
for (i, poll) in polls.enumerated() {
|
||||
let result = pollResults[i]
|
||||
|
|
@ -231,12 +242,12 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
if translated.0.isEmpty {
|
||||
translated = (poll.0.options[i].text, poll.0.options[i].entities)
|
||||
}
|
||||
attrOptions.append(.init(text: translated.0, entities: translated.1))
|
||||
attrOptions.append(TranslationMessageAttribute.Additional(text: translated.0, entities: translated.1))
|
||||
}
|
||||
|
||||
let solution: TranslationMessageAttribute.Additional?
|
||||
if result.count > 1 + poll.0.options.count, !result[result.count - 1].0.isEmpty {
|
||||
solution = .init(text: result[result.count - 1].0, entities: result[result.count - 1].1)
|
||||
solution = TranslationMessageAttribute.Additional(text: result[result.count - 1].0, entities: result[result.count - 1].1)
|
||||
} else {
|
||||
solution = nil
|
||||
}
|
||||
|
|
@ -251,6 +262,22 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !audioTranscriptionsResults.isEmpty {
|
||||
for (i, audioTranscription) in audioTranscriptions.enumerated() {
|
||||
if let result = audioTranscriptionsResults[i] {
|
||||
transaction.updateMessage(audioTranscription.1, update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes.filter { !($0 is TranslationMessageAttribute) }
|
||||
|
||||
let updatedAttribute: TranslationMessageAttribute = TranslationMessageAttribute(text: result.0, entities: result.1, additional: [], pollSolution: nil, toLang: toLang)
|
||||
attributes.append(updatedAttribute)
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> castError(TranslationError.self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
case lockedUntilDate
|
||||
case auctionSlug
|
||||
case auctionGiftsPerRound
|
||||
case auctionStartDate
|
||||
case upgradeVariantsCount
|
||||
}
|
||||
|
||||
public struct Availability: Equatable, Codable, PostboxCoding {
|
||||
|
|
@ -187,8 +189,10 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let lockedUntilDate: Int32?
|
||||
public let auctionSlug: String?
|
||||
public let auctionGiftsPerRound: Int32?
|
||||
public let auctionStartDate: Int32?
|
||||
public let upgradeVariantsCount: Int32?
|
||||
|
||||
public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?, perUserLimit: PerUserLimit?, lockedUntilDate: Int32?, auctionSlug: String?, auctionGiftsPerRound: Int32?) {
|
||||
public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?, perUserLimit: PerUserLimit?, lockedUntilDate: Int32?, auctionSlug: String?, auctionGiftsPerRound: Int32?, auctionStartDate: Int32?, upgradeVariantsCount: Int32?) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.file = file
|
||||
|
|
@ -203,6 +207,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.lockedUntilDate = lockedUntilDate
|
||||
self.auctionSlug = auctionSlug
|
||||
self.auctionGiftsPerRound = auctionGiftsPerRound
|
||||
self.auctionStartDate = auctionStartDate
|
||||
self.upgradeVariantsCount = upgradeVariantsCount
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
|
@ -227,6 +233,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.lockedUntilDate = try container.decodeIfPresent(Int32.self, forKey: .lockedUntilDate)
|
||||
self.auctionSlug = try container.decodeIfPresent(String.self, forKey: .auctionSlug)
|
||||
self.auctionGiftsPerRound = try container.decodeIfPresent(Int32.self, forKey: .auctionGiftsPerRound)
|
||||
self.auctionStartDate = try container.decodeIfPresent(Int32.self, forKey: .auctionStartDate)
|
||||
self.upgradeVariantsCount = try container.decodeIfPresent(Int32.self, forKey: .upgradeVariantsCount)
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
|
|
@ -244,6 +252,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.lockedUntilDate = decoder.decodeOptionalInt32ForKey(CodingKeys.lockedUntilDate.rawValue)
|
||||
self.auctionSlug = decoder.decodeOptionalStringForKey(CodingKeys.auctionSlug.rawValue)
|
||||
self.auctionGiftsPerRound = decoder.decodeOptionalInt32ForKey(CodingKeys.auctionGiftsPerRound.rawValue)
|
||||
self.auctionStartDate = decoder.decodeOptionalInt32ForKey(CodingKeys.auctionStartDate.rawValue)
|
||||
self.upgradeVariantsCount = decoder.decodeOptionalInt32ForKey(CodingKeys.upgradeVariantsCount.rawValue)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
|
@ -267,6 +277,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
try container.encodeIfPresent(self.lockedUntilDate, forKey: .lockedUntilDate)
|
||||
try container.encodeIfPresent(self.auctionSlug, forKey: .auctionSlug)
|
||||
try container.encodeIfPresent(self.auctionGiftsPerRound, forKey: .auctionGiftsPerRound)
|
||||
try container.encodeIfPresent(self.auctionStartDate, forKey: .auctionStartDate)
|
||||
try container.encodeIfPresent(self.upgradeVariantsCount, forKey: .upgradeVariantsCount)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
|
|
@ -320,6 +332,16 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: CodingKeys.auctionGiftsPerRound.rawValue)
|
||||
}
|
||||
if let auctionStartDate = self.auctionStartDate {
|
||||
encoder.encodeInt32(auctionStartDate, forKey: CodingKeys.auctionStartDate.rawValue)
|
||||
} else {
|
||||
encoder.encodeNil(forKey: CodingKeys.auctionStartDate.rawValue)
|
||||
}
|
||||
if let upgradeVariantsCount = self.upgradeVariantsCount {
|
||||
encoder.encodeInt32(upgradeVariantsCount, forKey: CodingKeys.upgradeVariantsCount.rawValue)
|
||||
} else {
|
||||
encoder.encodeNil(forKey: CodingKeys.upgradeVariantsCount.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -342,6 +364,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
case releasedBy
|
||||
case valueAmount
|
||||
case valueCurrency
|
||||
case valueUsdAmount
|
||||
case flags
|
||||
case themePeerId
|
||||
case peerColor
|
||||
|
|
@ -632,13 +655,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let releasedBy: EnginePeer.Id?
|
||||
public let valueAmount: Int64?
|
||||
public let valueCurrency: String?
|
||||
public let valueUsdAmount: Int64?
|
||||
public let flags: Flags
|
||||
public let themePeerId: EnginePeer.Id?
|
||||
public let peerColor: PeerCollectibleColor?
|
||||
public let hostPeerId: EnginePeer.Id?
|
||||
public let minOfferStars: Int64?
|
||||
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?) {
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?) {
|
||||
self.id = id
|
||||
self.giftId = giftId
|
||||
self.title = title
|
||||
|
|
@ -653,6 +677,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.releasedBy = releasedBy
|
||||
self.valueAmount = valueAmount
|
||||
self.valueCurrency = valueCurrency
|
||||
self.valueUsdAmount = valueUsdAmount
|
||||
self.flags = flags
|
||||
self.themePeerId = themePeerId
|
||||
self.peerColor = peerColor
|
||||
|
|
@ -690,6 +715,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy)
|
||||
self.valueAmount = try container.decodeIfPresent(Int64.self, forKey: .valueAmount)
|
||||
self.valueCurrency = try container.decodeIfPresent(String.self, forKey: .valueCurrency)
|
||||
self.valueUsdAmount = try container.decodeIfPresent(Int64.self, forKey: .valueUsdAmount)
|
||||
self.flags = try container.decodeIfPresent(Int32.self, forKey: .flags).flatMap { Flags(rawValue: $0) } ?? []
|
||||
self.themePeerId = try container.decodeIfPresent(Int64.self, forKey: .themePeerId).flatMap { EnginePeer.Id($0) }
|
||||
self.peerColor = try container.decodeIfPresent(PeerCollectibleColor.self, forKey: .peerColor)
|
||||
|
|
@ -726,6 +752,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) }
|
||||
self.valueAmount = decoder.decodeOptionalInt64ForKey(CodingKeys.valueAmount.rawValue)
|
||||
self.valueCurrency = decoder.decodeOptionalStringForKey(CodingKeys.valueCurrency.rawValue)
|
||||
self.valueUsdAmount = decoder.decodeOptionalInt64ForKey(CodingKeys.valueUsdAmount.rawValue)
|
||||
self.flags = decoder.decodeOptionalInt32ForKey(CodingKeys.flags.rawValue).flatMap { Flags(rawValue: $0) } ?? []
|
||||
self.themePeerId = decoder.decodeOptionalInt64ForKey(CodingKeys.themePeerId.rawValue).flatMap { EnginePeer.Id($0) }
|
||||
self.peerColor = decoder.decodeCodable(PeerCollectibleColor.self, forKey: CodingKeys.peerColor.rawValue)
|
||||
|
|
@ -756,6 +783,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy)
|
||||
try container.encodeIfPresent(self.valueAmount, forKey: .valueAmount)
|
||||
try container.encodeIfPresent(self.valueCurrency, forKey: .valueCurrency)
|
||||
try container.encodeIfPresent(self.valueUsdAmount, forKey: .valueUsdAmount)
|
||||
try container.encode(self.flags.rawValue, forKey: .flags)
|
||||
try container.encodeIfPresent(self.themePeerId?.toInt64(), forKey: .themePeerId)
|
||||
try container.encodeIfPresent(self.peerColor, forKey: .peerColor)
|
||||
|
|
@ -802,6 +830,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
encoder.encodeNil(forKey: CodingKeys.valueAmount.rawValue)
|
||||
encoder.encodeNil(forKey: CodingKeys.valueCurrency.rawValue)
|
||||
}
|
||||
if let valueUsdAmount = self.valueUsdAmount {
|
||||
encoder.encodeInt64(valueUsdAmount, forKey: CodingKeys.valueUsdAmount.rawValue)
|
||||
} else {
|
||||
encoder.encodeNil(forKey: CodingKeys.valueUsdAmount.rawValue)
|
||||
}
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: CodingKeys.flags.rawValue)
|
||||
if let themePeerId = self.themePeerId {
|
||||
encoder.encodeInt64(themePeerId.toInt64(), forKey: CodingKeys.themePeerId.rawValue)
|
||||
|
|
@ -841,6 +874,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
releasedBy: self.releasedBy,
|
||||
valueAmount: self.valueAmount,
|
||||
valueCurrency: self.valueCurrency,
|
||||
valueUsdAmount: self.valueUsdAmount,
|
||||
flags: self.flags,
|
||||
themePeerId: self.themePeerId,
|
||||
peerColor: self.peerColor,
|
||||
|
|
@ -865,6 +899,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
releasedBy: self.releasedBy,
|
||||
valueAmount: self.valueAmount,
|
||||
valueCurrency: self.valueCurrency,
|
||||
valueUsdAmount: self.valueUsdAmount,
|
||||
flags: self.flags,
|
||||
themePeerId: self.themePeerId,
|
||||
peerColor: self.peerColor,
|
||||
|
|
@ -889,6 +924,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
releasedBy: self.releasedBy,
|
||||
valueAmount: self.valueAmount,
|
||||
valueCurrency: self.valueCurrency,
|
||||
valueUsdAmount: self.valueUsdAmount,
|
||||
flags: self.flags,
|
||||
themePeerId: themePeerId,
|
||||
peerColor: self.peerColor,
|
||||
|
|
@ -913,6 +949,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
releasedBy: self.releasedBy,
|
||||
valueAmount: self.valueAmount,
|
||||
valueCurrency: self.valueCurrency,
|
||||
valueUsdAmount: self.valueUsdAmount,
|
||||
flags: self.flags,
|
||||
themePeerId: self.themePeerId,
|
||||
peerColor: self.peerColor,
|
||||
|
|
@ -1006,7 +1043,7 @@ public extension StarGift {
|
|||
extension StarGift {
|
||||
init?(apiStarGift: Api.StarGift) {
|
||||
switch apiStarGift {
|
||||
case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, perUserTotal, perUserRemains, lockedUntilDate, auctionSlug, giftsPerRound):
|
||||
case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, perUserTotal, perUserRemains, lockedUntilDate, auctionSlug, giftsPerRound, auctionStartDate, upgradeVariantsCount):
|
||||
var flags = StarGift.Gift.Flags()
|
||||
if (apiFlags & (1 << 2)) != 0 {
|
||||
flags.insert(.isBirthdayGift)
|
||||
|
|
@ -1041,8 +1078,25 @@ extension StarGift {
|
|||
guard let file = telegramMediaFileFromApiDocument(sticker, altDocuments: nil) else {
|
||||
return nil
|
||||
}
|
||||
self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit, lockedUntilDate: lockedUntilDate, auctionSlug: auctionSlug, auctionGiftsPerRound: giftsPerRound))
|
||||
case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, themePeer, peerColor, hostPeerId, minOfferStars):
|
||||
self = .generic(StarGift.Gift(
|
||||
id: id,
|
||||
title: title,
|
||||
file: file,
|
||||
price: stars,
|
||||
convertStars: convertStars,
|
||||
availability: availability,
|
||||
soldOut: soldOut,
|
||||
flags: flags,
|
||||
upgradeStars: upgradeStars,
|
||||
releasedBy: releasedBy?.peerId,
|
||||
perUserLimit: perUserLimit,
|
||||
lockedUntilDate: lockedUntilDate,
|
||||
auctionSlug: auctionSlug,
|
||||
auctionGiftsPerRound: giftsPerRound,
|
||||
auctionStartDate: auctionStartDate,
|
||||
upgradeVariantsCount: upgradeVariantsCount
|
||||
))
|
||||
case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, valueUsdAmount, themePeer, peerColor, hostPeerId, minOfferStars):
|
||||
let owner: StarGift.UniqueGift.Owner
|
||||
if let ownerAddress {
|
||||
owner = .address(ownerAddress)
|
||||
|
|
@ -1074,7 +1128,29 @@ extension StarGift {
|
|||
break
|
||||
}
|
||||
|
||||
self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (apiFlags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency, flags: flags, themePeerId: themePeer?.peerId, peerColor: peerCollectibleColor, hostPeerId: hostPeerId?.peerId, minOfferStars: minOfferStars.flatMap { Int64($0) }))
|
||||
self = .unique(StarGift.UniqueGift(
|
||||
id: id,
|
||||
giftId: giftId,
|
||||
title: title,
|
||||
number: num,
|
||||
slug: slug,
|
||||
owner: owner,
|
||||
attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0)
|
||||
},
|
||||
availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal),
|
||||
giftAddress: giftAddress,
|
||||
resellAmounts: resellAmounts,
|
||||
resellForTonOnly: (apiFlags & (1 << 7)) != 0,
|
||||
releasedBy: releasedBy?.peerId,
|
||||
valueAmount: valueAmount,
|
||||
valueCurrency: valueCurrency,
|
||||
valueUsdAmount: valueUsdAmount,
|
||||
flags: flags,
|
||||
themePeerId: themePeer?.peerId,
|
||||
peerColor: peerCollectibleColor,
|
||||
hostPeerId: hostPeerId?.peerId,
|
||||
minOfferStars: minOfferStars.flatMap { Int64($0) }
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1439,7 +1515,8 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star
|
|||
collectionIds: nil,
|
||||
prepaidUpgradeHash: nil,
|
||||
upgradeSeparate: false,
|
||||
dropOriginalDetailsStars: dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: dropOriginalDetailsStars,
|
||||
number: nil
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -2393,6 +2470,7 @@ public final class ProfileGiftsContext {
|
|||
case prepaidUpgradeHash
|
||||
case upgradeSeparate
|
||||
case dropOriginalDetailsStars
|
||||
case number
|
||||
}
|
||||
|
||||
public let gift: TelegramCore.StarGift
|
||||
|
|
@ -2415,6 +2493,7 @@ public final class ProfileGiftsContext {
|
|||
public let prepaidUpgradeHash: String?
|
||||
public let upgradeSeparate: Bool
|
||||
public let dropOriginalDetailsStars: Int64?
|
||||
public let number: Int32?
|
||||
|
||||
fileprivate let _fromPeerId: EnginePeer.Id?
|
||||
|
||||
|
|
@ -2442,7 +2521,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: [Int32]?,
|
||||
prepaidUpgradeHash: String?,
|
||||
upgradeSeparate: Bool,
|
||||
dropOriginalDetailsStars: Int64?
|
||||
dropOriginalDetailsStars: Int64?,
|
||||
number: Int32?
|
||||
) {
|
||||
self.gift = gift
|
||||
self.reference = reference
|
||||
|
|
@ -2465,6 +2545,7 @@ public final class ProfileGiftsContext {
|
|||
self.prepaidUpgradeHash = prepaidUpgradeHash
|
||||
self.upgradeSeparate = upgradeSeparate
|
||||
self.dropOriginalDetailsStars = dropOriginalDetailsStars
|
||||
self.number = number
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
|
@ -2497,6 +2578,7 @@ public final class ProfileGiftsContext {
|
|||
self.prepaidUpgradeHash = try container.decodeIfPresent(String.self, forKey: .prepaidUpgradeHash)
|
||||
self.upgradeSeparate = try container.decodeIfPresent(Bool.self, forKey: .upgradeSeparate) ?? false
|
||||
self.dropOriginalDetailsStars = try container.decodeIfPresent(Int64.self, forKey: .dropOriginalDetailsStars)
|
||||
self.number = try container.decodeIfPresent(Int32.self, forKey: .number)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
|
@ -2522,6 +2604,7 @@ public final class ProfileGiftsContext {
|
|||
try container.encodeIfPresent(self.prepaidUpgradeHash, forKey: .prepaidUpgradeHash)
|
||||
try container.encode(self.upgradeSeparate, forKey: .upgradeSeparate)
|
||||
try container.encodeIfPresent(self.dropOriginalDetailsStars, forKey: .dropOriginalDetailsStars)
|
||||
try container.encodeIfPresent(self.number, forKey: .number)
|
||||
}
|
||||
|
||||
public func withGift(_ gift: TelegramCore.StarGift) -> StarGift {
|
||||
|
|
@ -2545,7 +2628,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: self.collectionIds,
|
||||
prepaidUpgradeHash: self.prepaidUpgradeHash,
|
||||
upgradeSeparate: self.upgradeSeparate,
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars,
|
||||
number: self.number
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2570,7 +2654,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: self.collectionIds,
|
||||
prepaidUpgradeHash: self.prepaidUpgradeHash,
|
||||
upgradeSeparate: self.upgradeSeparate,
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars,
|
||||
number: self.number
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2595,7 +2680,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: self.collectionIds,
|
||||
prepaidUpgradeHash: self.prepaidUpgradeHash,
|
||||
upgradeSeparate: self.upgradeSeparate,
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars,
|
||||
number: self.number
|
||||
)
|
||||
}
|
||||
fileprivate func withFromPeer(_ fromPeer: EnginePeer?) -> StarGift {
|
||||
|
|
@ -2619,7 +2705,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: self.collectionIds,
|
||||
prepaidUpgradeHash: self.prepaidUpgradeHash,
|
||||
upgradeSeparate: self.upgradeSeparate,
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars,
|
||||
number: self.number
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2644,7 +2731,8 @@ public final class ProfileGiftsContext {
|
|||
collectionIds: collectionIds,
|
||||
prepaidUpgradeHash: self.prepaidUpgradeHash,
|
||||
upgradeSeparate: self.upgradeSeparate,
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: self.dropOriginalDetailsStars,
|
||||
number: self.number
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2862,7 +2950,7 @@ public final class ProfileGiftsContext {
|
|||
extension ProfileGiftsContext.State.StarGift {
|
||||
init?(apiSavedStarGift: Api.SavedStarGift, peerId: EnginePeer.Id, transaction: Transaction) {
|
||||
switch apiSavedStarGift {
|
||||
case let .savedStarGift(flags, fromId, date, apiGift, message, msgId, savedId, convertStars, upgradeStars, canExportDate, transferStars, canTransferAt, canResaleAt, collectionIds, prepaidUpgradeHash, dropOriginalDetailsStars):
|
||||
case let .savedStarGift(flags, fromId, date, apiGift, message, msgId, savedId, convertStars, upgradeStars, canExportDate, transferStars, canTransferAt, canResaleAt, collectionIds, prepaidUpgradeHash, dropOriginalDetailsStars, number):
|
||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -2912,6 +3000,7 @@ extension ProfileGiftsContext.State.StarGift {
|
|||
self.prepaidUpgradeHash = prepaidUpgradeHash
|
||||
self.upgradeSeparate = (flags & (1 << 17)) != 0
|
||||
self.dropOriginalDetailsStars = dropOriginalDetailsStars
|
||||
self.number = number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3640,3 +3729,65 @@ public final class ResaleGiftsContext {
|
|||
return state
|
||||
}
|
||||
}
|
||||
|
||||
func giftUpgradesId(giftId: Int64) -> ItemCacheEntryId {
|
||||
let cacheKey = ValueBoxKey(length: 8)
|
||||
cacheKey.setInt64(0, value: giftId)
|
||||
return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedGiftUpgradesAttributes, key: cacheKey)
|
||||
}
|
||||
|
||||
final class CachedStartGiftUpgradeAttributes: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case attributes
|
||||
}
|
||||
|
||||
var attributes: [StarGift.UniqueGift.Attribute]
|
||||
|
||||
init(attributes: [StarGift.UniqueGift.Attribute]) {
|
||||
self.attributes = attributes
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.attributes = try container.decode([StarGift.UniqueGift.Attribute].self, forKey: .attributes)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.attributes, forKey: .attributes)
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_getStarGiftUpgradeAttributes(account: Account, giftId: Int64) -> Signal<[StarGift.UniqueGift.Attribute]?, NoError> {
|
||||
return account.postbox.transaction { transaction in
|
||||
if let cachedGifts = transaction.retrieveItemCacheEntry(id: giftUpgradesId(giftId: giftId))?.get(CachedStartGiftUpgradeAttributes.self) {
|
||||
return .single(cachedGifts.attributes)
|
||||
} else {
|
||||
return account.network.request(Api.functions.payments.getStarGiftUpgradeAttributes(giftId: giftId))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.payments.StarGiftUpgradeAttributes?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[StarGift.UniqueGift.Attribute]?, NoError> in
|
||||
guard let result else {
|
||||
return .single(nil)
|
||||
}
|
||||
switch result {
|
||||
case let .starGiftUpgradeAttributes(apiAttributes):
|
||||
let attributes = apiAttributes.compactMap { StarGift.UniqueGift.Attribute(apiAttribute: $0) }
|
||||
return account.postbox.transaction { transaction in
|
||||
if !attributes.isEmpty {
|
||||
if let entry = CodableEntry(CachedStartGiftUpgradeAttributes(attributes: attributes)) {
|
||||
transaction.putItemCacheEntry(id: giftUpgradesId(giftId: giftId), entry: entry)
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ private func _internal_getStarGiftAuctionState(postbox: Postbox, network: Networ
|
|||
}
|
||||
return postbox.transaction { transaction -> (gift: StarGift, state: GiftAuctionContext.State.AuctionState?, myState: GiftAuctionContext.State.MyState, timeout: Int32)? in
|
||||
switch result {
|
||||
case let .starGiftAuctionState(apiGift, state, userState, timeout, users):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
||||
case let .starGiftAuctionState(apiGift, state, userState, timeout, users, chats):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(chats: chats, users: users))
|
||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -54,8 +54,27 @@ public final class GiftAuctionContext {
|
|||
public var date: Int32
|
||||
}
|
||||
|
||||
public enum Round: Equatable {
|
||||
case generic(num: Int32, duration: Int32)
|
||||
case extendable(num: Int32, duration: Int32, extendTop: Int32, extendWindow: Int32)
|
||||
|
||||
public var num: Int32 {
|
||||
switch self {
|
||||
case let .generic(num, _), let .extendable(num, _, _, _):
|
||||
return num
|
||||
}
|
||||
}
|
||||
|
||||
public var duration: Int32 {
|
||||
switch self {
|
||||
case let .generic(_, duration), let .extendable(_, duration, _, _):
|
||||
return duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AuctionState: Equatable {
|
||||
case ongoing(version: Int32, startDate: Int32, endDate: Int32, minBidAmount: Int64, bidLevels: [BidLevel], topBidders: [EnginePeer], nextRoundDate: Int32, giftsLeft: Int32, currentRound: Int32, totalRounds: Int32)
|
||||
case ongoing(version: Int32, startDate: Int32, endDate: Int32, minBidAmount: Int64, bidLevels: [BidLevel], topBidders: [EnginePeer], nextRoundDate: Int32, giftsLeft: Int32, currentRound: Int32, totalRounds: Int32, rounds: [Round], lastGiftNumber: Int32)
|
||||
case finished(startDate: Int32, endDate: Int32, averagePrice: Int64)
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +155,7 @@ public final class GiftAuctionContext {
|
|||
|
||||
private var currentVersion: Int32 {
|
||||
var currentVersion: Int32 = 0
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = self.auctionState {
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _, _, _) = self.auctionState {
|
||||
currentVersion = version
|
||||
}
|
||||
return currentVersion
|
||||
|
|
@ -154,7 +173,7 @@ public final class GiftAuctionContext {
|
|||
return
|
||||
}
|
||||
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = auctionState, version < self.currentVersion {
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _, _, _) = auctionState, version < self.currentVersion {
|
||||
} else if let auctionState {
|
||||
self.auctionState = auctionState
|
||||
}
|
||||
|
|
@ -163,7 +182,7 @@ public final class GiftAuctionContext {
|
|||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
var effectiveTimeout = timeout
|
||||
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, _, _) = auctionState {
|
||||
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, _, _, _, _) = auctionState {
|
||||
let delta = nextRoundDate - currentTime
|
||||
if delta > 0 && delta < timeout {
|
||||
effectiveTimeout = delta
|
||||
|
|
@ -184,7 +203,7 @@ public final class GiftAuctionContext {
|
|||
}
|
||||
|
||||
func updateAuctionState(_ auctionState: GiftAuctionContext.State.AuctionState) {
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = auctionState, version < self.currentVersion {
|
||||
if case let .ongoing(version, _, _, _, _, _, _, _, _, _, _, _) = auctionState, version < self.currentVersion {
|
||||
} else {
|
||||
self.auctionState = auctionState
|
||||
}
|
||||
|
|
@ -225,14 +244,23 @@ extension GiftAuctionContext.State.BidLevel {
|
|||
extension GiftAuctionContext.State.AuctionState {
|
||||
init?(apiAuctionState: Api.StarGiftAuctionState, peers: [PeerId: Peer]) {
|
||||
switch apiAuctionState {
|
||||
case let .starGiftAuctionState(version, startDate, endDate, minBidAmount, bidLevels, topBiddersPeerIds, nextRoundAt, giftsLeft, currentRound, totalRounds):
|
||||
case let .starGiftAuctionState(version, startDate, endDate, minBidAmount, bidLevels, topBiddersPeerIds, nextRoundAt, lastGiftNumber, giftsLeft, currentRound, totalRounds, apiRounds):
|
||||
var topBidders: [EnginePeer] = []
|
||||
for peerId in topBiddersPeerIds {
|
||||
if let peer = peers[PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(peerId))] {
|
||||
topBidders.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
self = .ongoing(version: version, startDate: startDate, endDate: endDate, minBidAmount: minBidAmount, bidLevels: bidLevels.map(GiftAuctionContext.State.BidLevel.init(apiBidLevel:)), topBidders: topBidders, nextRoundDate: nextRoundAt, giftsLeft: giftsLeft, currentRound: currentRound, totalRounds: totalRounds)
|
||||
var rounds: [GiftAuctionContext.State.Round] = []
|
||||
for apiRound in apiRounds {
|
||||
switch apiRound {
|
||||
case let .starGiftAuctionRound(num, duration):
|
||||
rounds.append(.generic(num: num, duration: duration))
|
||||
case let .starGiftAuctionRoundExtendable(num, duration, extendTop, extendWindow):
|
||||
rounds.append(.extendable(num: num, duration: duration, extendTop: extendTop, extendWindow: extendWindow))
|
||||
}
|
||||
}
|
||||
self = .ongoing(version: version, startDate: startDate, endDate: endDate, minBidAmount: minBidAmount, bidLevels: bidLevels.map(GiftAuctionContext.State.BidLevel.init(apiBidLevel:)), topBidders: topBidders, nextRoundDate: nextRoundAt, giftsLeft: giftsLeft, currentRound: currentRound, totalRounds: totalRounds, rounds: rounds, lastGiftNumber: lastGiftNumber)
|
||||
case let .starGiftAuctionStateFinished(startDate, endDate, averagePrice):
|
||||
self = .finished(startDate: startDate, endDate: endDate, averagePrice: averagePrice)
|
||||
case .starGiftAuctionStateNotModified:
|
||||
|
|
@ -242,14 +270,23 @@ extension GiftAuctionContext.State.AuctionState {
|
|||
|
||||
init?(apiAuctionState: Api.StarGiftAuctionState, transaction: Transaction) {
|
||||
switch apiAuctionState {
|
||||
case let .starGiftAuctionState(version, startDate, endDate, minBidAmount, bidLevels, topBiddersPeerIds, nextRoundAt, giftsLeft, currentRound, totalRounds):
|
||||
case let .starGiftAuctionState(version, startDate, endDate, minBidAmount, bidLevels, topBiddersPeerIds, nextRoundAt, lastGiftNumber, giftsLeft, currentRound, totalRounds, apiRounds):
|
||||
var topBidders: [EnginePeer] = []
|
||||
for peerId in topBiddersPeerIds {
|
||||
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(peerId))) {
|
||||
topBidders.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
self = .ongoing(version: version, startDate: startDate, endDate: endDate, minBidAmount: minBidAmount, bidLevels: bidLevels.map(GiftAuctionContext.State.BidLevel.init(apiBidLevel:)), topBidders: topBidders, nextRoundDate: nextRoundAt, giftsLeft: giftsLeft, currentRound: currentRound, totalRounds: totalRounds)
|
||||
var rounds: [GiftAuctionContext.State.Round] = []
|
||||
for apiRound in apiRounds {
|
||||
switch apiRound {
|
||||
case let .starGiftAuctionRound(num, duration):
|
||||
rounds.append(.generic(num: num, duration: duration))
|
||||
case let .starGiftAuctionRoundExtendable(num, duration, extendTop, extendWindow):
|
||||
rounds.append(.extendable(num: num, duration: duration, extendTop: extendTop, extendWindow: extendWindow))
|
||||
}
|
||||
}
|
||||
self = .ongoing(version: version, startDate: startDate, endDate: endDate, minBidAmount: minBidAmount, bidLevels: bidLevels.map(GiftAuctionContext.State.BidLevel.init(apiBidLevel:)), topBidders: topBidders, nextRoundDate: nextRoundAt, giftsLeft: giftsLeft, currentRound: currentRound, totalRounds: totalRounds, rounds: rounds, lastGiftNumber: lastGiftNumber)
|
||||
case let .starGiftAuctionStateFinished(startDate, endDate, averagePrice):
|
||||
self = .finished(startDate: startDate, endDate: endDate, averagePrice: averagePrice)
|
||||
case .starGiftAuctionStateNotModified:
|
||||
|
|
@ -281,6 +318,7 @@ public struct GiftAuctionAcquiredGift: Equatable {
|
|||
public let position: Int32
|
||||
public let text: String?
|
||||
public let entities: [MessageTextEntity]?
|
||||
public let number: Int32?
|
||||
}
|
||||
|
||||
func _internal_getGiftAuctionAcquiredGifts(account: Account, giftId: Int64) -> Signal<[GiftAuctionAcquiredGift], NoError> {
|
||||
|
|
@ -302,7 +340,7 @@ func _internal_getGiftAuctionAcquiredGifts(account: Account, giftId: Int64) -> S
|
|||
var mappedGifts: [GiftAuctionAcquiredGift] = []
|
||||
for gift in gifts {
|
||||
switch gift {
|
||||
case let .starGiftAuctionAcquiredGift(flags, peerId, date, bidAmount, round, pos, message):
|
||||
case let .starGiftAuctionAcquiredGift(flags, peerId, date, bidAmount, round, pos, message, number):
|
||||
if let peer = transaction.getPeer(peerId.peerId) {
|
||||
var text: String?
|
||||
var entities: [MessageTextEntity]?
|
||||
|
|
@ -321,7 +359,8 @@ func _internal_getGiftAuctionAcquiredGifts(account: Account, giftId: Int64) -> S
|
|||
round: round,
|
||||
position: pos,
|
||||
text: text,
|
||||
entities: entities
|
||||
entities: entities,
|
||||
number: number
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -338,8 +377,8 @@ func _internal_getActiveGiftAuctions(account: Account, hash: Int64) -> Signal<[G
|
|||
|> mapToSignal { result in
|
||||
return account.postbox.transaction { transaction -> [GiftAuctionContext]? in
|
||||
switch result {
|
||||
case let .starGiftActiveAuctions(auctions, users):
|
||||
let parsedPeers = AccumulatedPeers(users: users)
|
||||
case let .starGiftActiveAuctions(auctions, users, chats):
|
||||
let parsedPeers = AccumulatedPeers(chats: chats, users: users)
|
||||
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers)
|
||||
|
||||
var auctionContexts: [GiftAuctionContext] = []
|
||||
|
|
@ -493,7 +532,7 @@ public class GiftAuctionsManager {
|
|||
|
||||
public extension GiftAuctionContext.State {
|
||||
func getPlace(myBid: Int64?, myBidDate: Int32?) -> Int32? {
|
||||
guard case let .ongoing(_, _, _, _, bidLevels, _, _, _, _, _) = self.auctionState else {
|
||||
guard case let .ongoing(_, _, _, _, bidLevels, _, _, _, _, _, _, _) = self.auctionState else {
|
||||
return nil
|
||||
}
|
||||
guard let myBid = myBid ?? self.myState.bidAmount else {
|
||||
|
|
@ -550,7 +589,7 @@ public extension GiftAuctionContext.State {
|
|||
|
||||
var startDate: Int32 {
|
||||
switch self.auctionState {
|
||||
case let .ongoing(_, startDate, _, _, _, _, _, _, _, _):
|
||||
case let .ongoing(_, startDate, _, _, _, _, _, _, _, _, _, _):
|
||||
return startDate
|
||||
case let .finished(startDate, _, _):
|
||||
return startDate
|
||||
|
|
@ -559,7 +598,7 @@ public extension GiftAuctionContext.State {
|
|||
|
||||
var endDate: Int32 {
|
||||
switch self.auctionState {
|
||||
case let .ongoing(_, _, endDate, _, _, _, _, _, _, _):
|
||||
case let .ongoing(_, _, endDate, _, _, _, _, _, _, _, _, _):
|
||||
return endDate
|
||||
case let .finished(_, endDate, _):
|
||||
return endDate
|
||||
|
|
|
|||
|
|
@ -1671,7 +1671,8 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
|
|||
collectionIds: nil,
|
||||
prepaidUpgradeHash: nil,
|
||||
upgradeSeparate: false,
|
||||
dropOriginalDetailsStars: dropOriginalDetailsStars
|
||||
dropOriginalDetailsStars: dropOriginalDetailsStars,
|
||||
number: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,10 @@ public extension TelegramEngine {
|
|||
public func sendStarGiftOffer(peerId: EnginePeer.Id, slug: String, amount: CurrencyAmount, duration: Int32, allowPaidStars: Int64?) -> Signal<Never, SendStarGiftOfferError> {
|
||||
return _internal_sendStarGiftOffer(account: self.account, peerId: peerId, slug: slug, amount: amount, duration: duration, allowPaidStars: allowPaidStars)
|
||||
}
|
||||
|
||||
public func getStarGiftUpgradeAttributes(giftId: Int64) -> Signal<[StarGift.UniqueGift.Attribute]?, NoError> {
|
||||
return _internal_getStarGiftUpgradeAttributes(account: self.account, giftId: giftId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1171,7 +1171,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = mutableString
|
||||
case .prizeStars:
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, isPrepaidUpgrade, _, peerId, senderId, _, _, _, upgradeSeparate, isAuctionAcquired, _):
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, isPrepaidUpgrade, _, peerId, senderId, _, _, _, upgradeSeparate, isAuctionAcquired, _, _):
|
||||
if !forAdditionalServiceMessage {
|
||||
if let text {
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
|
||||
|
|
@ -1262,7 +1262,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, assigned, _):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, assigned, fromOffer):
|
||||
if case let .unique(gift) = gift {
|
||||
if !forAdditionalServiceMessage && !"".isEmpty {
|
||||
attributedString = NSAttributedString(string: "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))", font: titleFont, textColor: primaryTextColor)
|
||||
|
|
@ -1290,7 +1290,33 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if message.id.peerId == accountPeerId && assigned {
|
||||
if fromOffer, let resaleStars {
|
||||
let starsString: String
|
||||
switch resaleStars.currency {
|
||||
case .stars:
|
||||
starsString = strings.Notification_StarsGiftOffer_Accepted_Stars(Int32(clamping: resaleStars.amount.value))
|
||||
case .ton:
|
||||
starsString = formatTonAmountText(resaleStars.amount.value, dateTimeFormat: dateTimeFormat) + " TON"
|
||||
}
|
||||
let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))"
|
||||
var peerName = ""
|
||||
if let name = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle {
|
||||
peerName = name
|
||||
}
|
||||
if message.author?.id == accountPeerId {
|
||||
let peerIds: [(Int, EnginePeer.Id?)] = [(1, message.id.peerId)]
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[0] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_AcceptedYou(giftTitle, peerName, starsString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
} else {
|
||||
let peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)]
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Accepted(peerName, giftTitle, starsString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
} else if message.id.peerId == accountPeerId && assigned {
|
||||
let attributes: [Int: MarkdownAttributeSet] = [0: boldAttributes]
|
||||
let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))"
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Assigned(giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
|
|
@ -1616,10 +1642,103 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, price)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case .starGiftPurchaseOffer:
|
||||
attributedString = nil
|
||||
case .starGiftPurchaseOfferDeclined:
|
||||
attributedString = nil
|
||||
case let .starGiftPurchaseOffer(gift, amount, _, _, _):
|
||||
let peerName = message.peers[message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
|
||||
let giftTitle: String
|
||||
if case let .unique(gift) = gift {
|
||||
giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: dateTimeFormat))"
|
||||
} else {
|
||||
giftTitle = ""
|
||||
}
|
||||
|
||||
let peerIds: [(Int, EnginePeer.Id?)] = [(0, message.id.peerId)]
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
|
||||
if message.author?.id == accountPeerId {
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = strings.Notification_StarsGiftOffer_OfferYou_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_OfferYou(peerName, priceString, giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
} else {
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = strings.Notification_StarsGiftOffer_Offer_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Offer(peerName, priceString, giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
case let .starGiftPurchaseOfferDeclined(gift, amount, hasExpired):
|
||||
let peerName = message.peers[message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
let peerIds: [(Int, EnginePeer.Id?)] = [(0, message.id.peerId)]
|
||||
|
||||
let giftTitle: String
|
||||
if case let .unique(gift) = gift {
|
||||
giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: dateTimeFormat))"
|
||||
} else {
|
||||
giftTitle = ""
|
||||
}
|
||||
|
||||
if hasExpired {
|
||||
if message.author?.id == accountPeerId {
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = strings.Notification_StarsGiftOffer_ExpiredYou_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_ExpiredYou(peerName, giftTitle, priceString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
} else {
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = strings.Notification_StarsGiftOffer_Expired_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
let timeString = "[TODO]"
|
||||
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
attributes[3] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Expired(peerName, giftTitle, timeString, priceString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
} else {
|
||||
if message.author?.id == accountPeerId {
|
||||
let attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_RejectedYou(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
} else {
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = strings.Notification_StarsGiftOffer_Rejected_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributes[2] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Rejected(peerName, giftTitle, priceString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case .unknown:
|
||||
attributedString = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatRecentActionsController",
|
||||
|
|
|
|||
|
|
@ -18,23 +18,29 @@ import ComponentFlow
|
|||
import GlassBarButtonComponent
|
||||
import BundleIconComponent
|
||||
import EdgeEffect
|
||||
import SaveToCameraRoll
|
||||
|
||||
private final class AttachmentFileControllerArguments {
|
||||
let context: AccountContext
|
||||
let isAudio: Bool
|
||||
let openGallery: () -> Void
|
||||
let openFiles: () -> Void
|
||||
let expandSavedMusic: () -> Void
|
||||
let send: (Message) -> Void
|
||||
|
||||
init(context: AccountContext, openGallery: @escaping () -> Void, openFiles: @escaping () -> Void, send: @escaping (Message) -> Void) {
|
||||
init(context: AccountContext, isAudio: Bool, openGallery: @escaping () -> Void, openFiles: @escaping () -> Void, expandSavedMusic: @escaping () -> Void, send: @escaping (Message) -> Void) {
|
||||
self.context = context
|
||||
self.isAudio = isAudio
|
||||
self.openGallery = openGallery
|
||||
self.openFiles = openFiles
|
||||
self.expandSavedMusic = expandSavedMusic
|
||||
self.send = send
|
||||
}
|
||||
}
|
||||
|
||||
private enum AttachmentFileSection: Int32 {
|
||||
case select
|
||||
case savedMusic
|
||||
case recent
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +61,10 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
case selectFromGallery(PresentationTheme, String)
|
||||
case selectFromFiles(PresentationTheme, String)
|
||||
|
||||
case savedHeader(PresentationTheme, String)
|
||||
case savedFile(Int32, PresentationTheme, Message?)
|
||||
case showMore(PresentationTheme, String)
|
||||
|
||||
case recentHeader(PresentationTheme, String)
|
||||
case file(Int32, PresentationTheme, Message?)
|
||||
|
||||
|
|
@ -62,6 +72,8 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
switch self {
|
||||
case .selectFromGallery, .selectFromFiles:
|
||||
return AttachmentFileSection.select.rawValue
|
||||
case .savedHeader, .savedFile, .showMore:
|
||||
return AttachmentFileSection.savedMusic.rawValue
|
||||
case .recentHeader, .file:
|
||||
return AttachmentFileSection.recent.rawValue
|
||||
}
|
||||
|
|
@ -73,10 +85,16 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
return 0
|
||||
case .selectFromFiles:
|
||||
return 1
|
||||
case .recentHeader:
|
||||
case .savedHeader:
|
||||
return 2
|
||||
case let .file(index, _, _):
|
||||
case let .savedFile(index, _, _):
|
||||
return 3 + index
|
||||
case .showMore:
|
||||
return 9999
|
||||
case .recentHeader:
|
||||
return 10000
|
||||
case let .file(index, _, _):
|
||||
return 10001 + index
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +112,24 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .savedHeader(lhsTheme, lhsText):
|
||||
if case let .savedHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .savedFile(lhsIndex, lhsTheme, lhsMessage):
|
||||
if case let .savedFile(rhsIndex, rhsTheme, rhsMessage) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, areMessagesEqual(lhsMessage, rhsMessage) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .showMore(lhsTheme, lhsText):
|
||||
if case let .showMore(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .recentHeader(lhsTheme, lhsText):
|
||||
if case let .recentHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
|
|
@ -124,6 +160,22 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.cloudIcon(presentationData.theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
|
||||
arguments.openFiles()
|
||||
})
|
||||
|
||||
case let .savedHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .savedFile(_, _, message):
|
||||
let interaction = ListMessageItemInteraction(openMessage: { message, _ in
|
||||
arguments.send(message)
|
||||
return false
|
||||
}, openMessageContextMenu: { _, _, _, _, _ in }, toggleMessagesSelection: { _, _ in }, openUrl: { _, _, _, _ in }, openInstantPage: { _, _ in }, longTap: { _, _ in }, getHiddenMedia: { return [:] })
|
||||
|
||||
let dateTimeFormat = arguments.context.sharedContext.currentPresentationData.with({$0}).dateTimeFormat
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .color(0)), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0, auxiliaryRadius: 0, mergeBubbleCorners: false))
|
||||
return ListMessageItem(presentationData: chatPresentationData, systemStyle: .glass, context: arguments.context, chatLocation: .peer(id: arguments.context.account.peerId), interaction: interaction, message: message, selection: .none, displayHeader: false, isDownloadList: arguments.isAudio, isStoryMusic: true, displayFileInfo: true, displayBackground: true, style: .blocks, sectionId: self.section)
|
||||
case let .showMore(theme, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: text, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandSavedMusic()
|
||||
})
|
||||
case let .recentHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .file(_, _, message):
|
||||
|
|
@ -135,22 +187,53 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
let dateTimeFormat = arguments.context.sharedContext.currentPresentationData.with({$0}).dateTimeFormat
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .color(0)), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0, auxiliaryRadius: 0, mergeBubbleCorners: false))
|
||||
|
||||
return ListMessageItem(presentationData: chatPresentationData, systemStyle: .glass, context: arguments.context, chatLocation: .peer(id: PeerId(0)), interaction: interaction, message: message, selection: .none, displayHeader: false, displayFileInfo: false, displayBackground: true, style: .blocks)
|
||||
return ListMessageItem(presentationData: chatPresentationData, systemStyle: .glass, context: arguments.context, chatLocation: .peer(id: PeerId(0)), interaction: interaction, message: message, selection: .none, displayHeader: false, isDownloadList: arguments.isAudio, isStoryMusic: true, displayFileInfo: true, displayBackground: true, style: .blocks, sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func attachmentFileControllerEntries(presentationData: PresentationData, recentDocuments: [Message]?, empty: Bool) -> [AttachmentFileEntry] {
|
||||
private func attachmentFileControllerEntries(presentationData: PresentationData, mode: AttachmentFileControllerMode, state: AttachmentFileControllerState, savedMusic: [Message]?, recentDocuments: [Message]?, empty: Bool) -> [AttachmentFileEntry] {
|
||||
guard !empty else {
|
||||
return []
|
||||
}
|
||||
var entries: [AttachmentFileEntry] = []
|
||||
entries.append(.selectFromGallery(presentationData.theme, presentationData.strings.Attachment_SelectFromGallery))
|
||||
if case .recent = mode {
|
||||
entries.append(.selectFromGallery(presentationData.theme, presentationData.strings.Attachment_SelectFromGallery))
|
||||
}
|
||||
entries.append(.selectFromFiles(presentationData.theme, presentationData.strings.Attachment_SelectFromFiles))
|
||||
|
||||
let listTitle: String
|
||||
switch mode {
|
||||
case .recent:
|
||||
listTitle = presentationData.strings.Attachment_RecentlySentFiles
|
||||
case .audio:
|
||||
//TODO:localize
|
||||
listTitle = "SHARED AUDIO"
|
||||
}
|
||||
|
||||
if case .audio = mode {
|
||||
if let savedMusic, savedMusic.count > 0 {
|
||||
entries.append(.savedHeader(presentationData.theme, "SAVED MUSIC".uppercased()))
|
||||
var savedMusic = savedMusic
|
||||
var showMore = false
|
||||
if savedMusic.count > 4 && !state.savedMusicExpanded {
|
||||
savedMusic = Array(savedMusic.prefix(3))
|
||||
showMore = true
|
||||
}
|
||||
var i: Int32 = 0
|
||||
for file in savedMusic {
|
||||
entries.append(.savedFile(i, presentationData.theme, file))
|
||||
i += 1
|
||||
}
|
||||
if showMore {
|
||||
entries.append(.showMore(presentationData.theme, "Show More"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let recentDocuments = recentDocuments {
|
||||
if recentDocuments.count > 0 {
|
||||
entries.append(.recentHeader(presentationData.theme, presentationData.strings.Attachment_RecentlySentFiles.uppercased()))
|
||||
entries.append(.recentHeader(presentationData.theme, listTitle.uppercased()))
|
||||
var i: Int32 = 0
|
||||
for file in recentDocuments {
|
||||
entries.append(.file(i, presentationData.theme, file))
|
||||
|
|
@ -158,7 +241,7 @@ private func attachmentFileControllerEntries(presentationData: PresentationData,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
entries.append(.recentHeader(presentationData.theme, presentationData.strings.Attachment_RecentlySentFiles.uppercased()))
|
||||
entries.append(.recentHeader(presentationData.theme, listTitle.uppercased()))
|
||||
for i in 0 ..< 11 {
|
||||
entries.append(.file(Int32(i), presentationData.theme, nil))
|
||||
}
|
||||
|
|
@ -185,6 +268,8 @@ public class AttachmentFileControllerImpl: ItemListController, AttachmentFileCon
|
|||
|
||||
var delayDisappear = false
|
||||
|
||||
var hasBottomEdgeEffect = true
|
||||
|
||||
var resetForReuseImpl: () -> Void = {}
|
||||
public func resetForReuse() {
|
||||
self.resetForReuseImpl()
|
||||
|
|
@ -229,31 +314,41 @@ public class AttachmentFileControllerImpl: ItemListController, AttachmentFileCon
|
|||
transition.updateFrame(view: topEdgeEffectView, frame: topEdgeEffectFrame)
|
||||
topEdgeEffectView.update(content: .clear, blur: true, alpha: 1.0, rect: topEdgeEffectFrame, edge: .top, edgeSize: topEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
|
||||
let bottomEdgeEffectView: EdgeEffectView
|
||||
if let current = self.bottomEdgeEffectView {
|
||||
bottomEdgeEffectView = current
|
||||
} else {
|
||||
bottomEdgeEffectView = EdgeEffectView()
|
||||
self.view.addSubview(bottomEdgeEffectView)
|
||||
self.bottomEdgeEffectView = bottomEdgeEffectView
|
||||
if self.hasBottomEdgeEffect {
|
||||
let bottomEdgeEffectView: EdgeEffectView
|
||||
if let current = self.bottomEdgeEffectView {
|
||||
bottomEdgeEffectView = current
|
||||
} else {
|
||||
bottomEdgeEffectView = EdgeEffectView()
|
||||
self.view.addSubview(bottomEdgeEffectView)
|
||||
self.bottomEdgeEffectView = bottomEdgeEffectView
|
||||
}
|
||||
|
||||
let bottomEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight - layout.additionalInsets.bottom), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
|
||||
transition.updateAlpha(layer: bottomEdgeEffectView.layer, alpha: self.isSearching ? 0.0 : 1.0)
|
||||
bottomEdgeEffectView.update(content: .clear, blur: true, alpha: 1.0, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: bottomEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
} else if let bottomEdgeEffectView = self.bottomEdgeEffectView {
|
||||
bottomEdgeEffectView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let bottomEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight - layout.additionalInsets.bottom), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
|
||||
transition.updateAlpha(layer: bottomEdgeEffectView.layer, alpha: self.isSearching ? 0.0 : 1.0)
|
||||
bottomEdgeEffectView.update(content: .clear, blur: true, alpha: 1.0, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: bottomEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
}
|
||||
}
|
||||
|
||||
private struct AttachmentFileControllerState: Equatable {
|
||||
var searching: Bool
|
||||
var savedMusicExpanded: Bool
|
||||
}
|
||||
|
||||
public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController {
|
||||
public enum AttachmentFileControllerMode {
|
||||
case recent
|
||||
case audio
|
||||
}
|
||||
|
||||
public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: AttachmentFileControllerMode = .recent, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController {
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let statePromise = ValuePromise(AttachmentFileControllerState(searching: false), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: AttachmentFileControllerState(searching: false))
|
||||
let statePromise = ValuePromise(AttachmentFileControllerState(searching: false, savedMusicExpanded: false), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: AttachmentFileControllerState(searching: false, savedMusicExpanded: false))
|
||||
let updateState: ((AttachmentFileControllerState) -> AttachmentFileControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
|
@ -265,39 +360,87 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
var updateIsSearchingImpl: ((Bool) -> Void)?
|
||||
let arguments = AttachmentFileControllerArguments(
|
||||
context: context,
|
||||
isAudio: mode == .audio,
|
||||
openGallery: {
|
||||
presentGallery()
|
||||
},
|
||||
openFiles: {
|
||||
presentFiles()
|
||||
},
|
||||
expandSavedMusic: {
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.savedMusicExpanded = true
|
||||
return updatedState
|
||||
}
|
||||
},
|
||||
send: { message in
|
||||
let _ = (context.engine.messages.getMessagesLoadIfNecessary([message.id], strategy: .cloud(skipLocal: true))
|
||||
|> `catch` { _ in
|
||||
return .single(.result([]))
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[Message], NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
if message.id.namespace == Namespaces.Message.Local {
|
||||
if let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile {
|
||||
send(.standalone(media: file))
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStandalone(next: { messages in
|
||||
if let message = messages.first, let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile {
|
||||
send(.message(message: MessageReference(message), media: file))
|
||||
} else {
|
||||
let _ = (context.engine.messages.getMessagesLoadIfNecessary([message.id], strategy: .cloud(skipLocal: true))
|
||||
|> `catch` { _ in
|
||||
return .single(.result([]))
|
||||
}
|
||||
dismissImpl?()
|
||||
})
|
||||
|> mapToSignal { result -> Signal<[Message], NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStandalone(next: { messages in
|
||||
if let message = messages.first, let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile {
|
||||
send(.message(message: MessageReference(message), media: file))
|
||||
}
|
||||
dismissImpl?()
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
let recentDocuments: Signal<[Message]?, NoError> = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .sentMedia(tags: [.file]), query: "", state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
let recentDocuments: Signal<[Message]?, NoError>
|
||||
let savedMusicContext: ProfileSavedMusicContext?
|
||||
let savedMusic: Signal<[Message]?, NoError>
|
||||
switch mode {
|
||||
case .recent:
|
||||
recentDocuments = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .sentMedia(tags: [.file]), query: "", state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
savedMusicContext = nil
|
||||
savedMusic = .single(nil)
|
||||
case .audio:
|
||||
recentDocuments = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil), query: "", state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
savedMusicContext = ProfileSavedMusicContext(account: context.account, peerId: context.account.peerId)
|
||||
savedMusic = .single(nil)
|
||||
|> then(
|
||||
savedMusicContext!.state
|
||||
|> map { state in
|
||||
let peerId = context.account.peerId
|
||||
var messages: [Message] = []
|
||||
let peers = SimpleDictionary<PeerId, Peer>()
|
||||
// if let peer {
|
||||
// peers[peerId] = peer._asPeer()
|
||||
// }
|
||||
for file in state.files {
|
||||
let stableId = UInt32(clamping: file.fileId.id % Int64(Int32.max))
|
||||
messages.append(Message(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: Int32(stableId)), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [.music], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]))
|
||||
}
|
||||
return messages
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
|
||||
|
|
@ -306,14 +449,12 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
|
||||
let previousRecentDocuments = Atomic<[Message]?>(value: nil)
|
||||
let signal = combineLatest(queue: Queue.mainQueue(),
|
||||
presentationData,
|
||||
recentDocuments,
|
||||
statePromise.get()
|
||||
)
|
||||
|> map {
|
||||
presentationData,
|
||||
recentDocuments,
|
||||
state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
savedMusic,
|
||||
statePromise.get()
|
||||
)
|
||||
|> map { presentationData, recentDocuments, savedMusic, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var presentationData = presentationData
|
||||
|
||||
let updatedTheme = presentationData.theme.withModalBlocksBackground()
|
||||
|
|
@ -397,9 +538,17 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
rightNavigationButton = searchButtonNode.flatMap { ItemListNavigationButton(content: .node($0), style: .regular, enabled: true, action: {}) }
|
||||
}
|
||||
|
||||
let title: String
|
||||
switch mode {
|
||||
case .recent:
|
||||
title = presentationData.strings.Attachment_File
|
||||
case .audio:
|
||||
title = "Audio"
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(
|
||||
presentationData: ItemListPresentationData(presentationData),
|
||||
title: .text(presentationData.strings.Attachment_File),
|
||||
title: .text(title),
|
||||
leftNavigationButton: leftNavigationButton,
|
||||
rightNavigationButton: rightNavigationButton,
|
||||
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back),
|
||||
|
|
@ -424,7 +573,7 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
|
||||
var searchItem: ItemListControllerSearch?
|
||||
if state.searching {
|
||||
searchItem = AttachmentFileSearchItem(context: context, presentationData: presentationData, focus: {
|
||||
searchItem = AttachmentFileSearchItem(context: context, mode: mode, presentationData: presentationData, focus: {
|
||||
expandImpl?()
|
||||
}, cancel: {
|
||||
updateState { state in
|
||||
|
|
@ -441,14 +590,18 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
})
|
||||
}
|
||||
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: attachmentFileControllerEntries(presentationData: presentationData, recentDocuments: recentDocuments, empty: bannedSendMedia != nil), style: .blocks, emptyStateItem: emptyItem, searchItem: searchItem, crossfadeState: crossfade, animateChanges: animateChanges)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: attachmentFileControllerEntries(presentationData: presentationData, mode: mode, state: state, savedMusic: savedMusic, recentDocuments: recentDocuments, empty: bannedSendMedia != nil), style: .blocks, emptyStateItem: emptyItem, searchItem: searchItem, crossfadeState: crossfade, animateChanges: animateChanges)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
let _ = savedMusicContext?.state
|
||||
}
|
||||
|
||||
let controller = AttachmentFileControllerImpl(context: context, state: signal, hideNavigationBarBackground: true)
|
||||
if case .audio = mode {
|
||||
controller.hasBottomEdgeEffect = false
|
||||
}
|
||||
controller.delayDisappear = true
|
||||
controller.visibleBottomContentOffsetChanged = { [weak controller] offset in
|
||||
switch offset {
|
||||
|
|
@ -490,3 +643,34 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre
|
|||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
public func storyAudioPickerController(
|
||||
context: AccountContext,
|
||||
selectFromFiles: @escaping () -> Void,
|
||||
dismissed: @escaping () -> Void,
|
||||
completion: @escaping (AnyMediaReference) -> Void,
|
||||
) -> ViewController {
|
||||
var dismissImpl: (() -> Void)?
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, style: .glass, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false)
|
||||
controller.requestController = { _, present in
|
||||
let filePickerController = makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, mode: .audio, bannedSendMedia: nil, presentGallery: {}, presentFiles: {
|
||||
selectFromFiles()
|
||||
dismissImpl?()
|
||||
}, send: { file in
|
||||
completion(file)
|
||||
dismissImpl?()
|
||||
}) as! AttachmentFileControllerImpl
|
||||
present(filePickerController, filePickerController.mediaPickerContext)
|
||||
}
|
||||
controller.navigationPresentation = .flatModal
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
controller.didDismiss = {
|
||||
dismissed()
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss(animated: true)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import SearchInputPanelComponent
|
|||
|
||||
final class AttachmentFileSearchItem: ItemListControllerSearch {
|
||||
let context: AccountContext
|
||||
let mode: AttachmentFileControllerMode
|
||||
let presentationData: PresentationData
|
||||
let focus: () -> Void
|
||||
let cancel: () -> Void
|
||||
|
|
@ -31,8 +32,9 @@ final class AttachmentFileSearchItem: ItemListControllerSearch {
|
|||
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
|
||||
private let activityDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, focus: @escaping () -> Void, cancel: @escaping () -> Void, send: @escaping (Message) -> Void, dismissInput: @escaping () -> Void) {
|
||||
init(context: AccountContext, mode: AttachmentFileControllerMode, presentationData: PresentationData, focus: @escaping () -> Void, cancel: @escaping () -> Void, send: @escaping (Message) -> Void, dismissInput: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.presentationData = presentationData
|
||||
self.focus = focus
|
||||
self.cancel = cancel
|
||||
|
|
@ -69,7 +71,7 @@ final class AttachmentFileSearchItem: ItemListControllerSearch {
|
|||
}
|
||||
|
||||
func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode {
|
||||
return AttachmentFileSearchItemNode(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, focus: self.focus, send: self.send, cancel: self.cancel, updateActivity: { [weak self] value in
|
||||
return AttachmentFileSearchItemNode(context: self.context, mode: self.mode, presentationData: self.presentationData, focus: self.focus, send: self.send, cancel: self.cancel, updateActivity: { [weak self] value in
|
||||
self?.activity.set(value)
|
||||
}, dismissInput: self.dismissInput)
|
||||
}
|
||||
|
|
@ -77,8 +79,8 @@ final class AttachmentFileSearchItem: ItemListControllerSearch {
|
|||
|
||||
private final class AttachmentFileSearchItemNode: ItemListControllerSearchNode {
|
||||
private let context: AccountContext
|
||||
private let theme: PresentationTheme
|
||||
private let strings: PresentationStrings
|
||||
private let mode: AttachmentFileControllerMode
|
||||
private let presentationData: PresentationData
|
||||
private let focus: () -> Void
|
||||
private let cancel: () -> Void
|
||||
|
||||
|
|
@ -88,14 +90,14 @@ private final class AttachmentFileSearchItemNode: ItemListControllerSearchNode {
|
|||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, focus: @escaping () -> Void, send: @escaping (Message) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, dismissInput: @escaping () -> Void) {
|
||||
init(context: AccountContext, mode: AttachmentFileControllerMode, presentationData: PresentationData, focus: @escaping () -> Void, send: @escaping (Message) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, dismissInput: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.mode = mode
|
||||
self.presentationData = presentationData
|
||||
self.focus = focus
|
||||
self.cancel = cancel
|
||||
|
||||
self.containerNode = AttachmentFileSearchContainerNode(context: context, forceTheme: nil, send: { message in
|
||||
self.containerNode = AttachmentFileSearchContainerNode(context: context, mode: mode, presentationData: presentationData, send: { message in
|
||||
send(message)
|
||||
}, updateActivity: updateActivity)
|
||||
|
||||
|
|
@ -136,15 +138,16 @@ private final class AttachmentFileSearchItemNode: ItemListControllerSearchNode {
|
|||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight)))
|
||||
self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight)), navigationBarHeight: 0.0, transition: transition)
|
||||
|
||||
//TODO:localize
|
||||
let searchInputSize = self.searchInput.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
SearchInputPanelComponent(
|
||||
theme: self.theme,
|
||||
strings: self.strings,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
metrics: layout.metrics,
|
||||
safeInsets: layout.safeInsets,
|
||||
placeholder: self.strings.Attachment_FilesSearchPlaceholder,
|
||||
placeholder: self.mode == .audio ? "Search shared audio" : self.presentationData.strings.Attachment_FilesSearchPlaceholder,
|
||||
updated: { [weak self] query in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -251,7 +254,7 @@ private final class AttachmentFileSearchEntry: Comparable, Identifiable {
|
|||
interaction.send(message)
|
||||
return false
|
||||
}, openMessageContextMenu: { _, _, _, _, _ in }, toggleMessagesSelection: { _, _ in }, openUrl: { _, _, _, _ in }, openInstantPage: { _, _ in }, longTap: { _, _ in }, getHiddenMedia: { return [:] })
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), systemStyle: .glass, context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), systemStyle: .glass, context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,10 +296,8 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
|||
private let emptyQueryDisposable = MetaDisposable()
|
||||
private let searchDisposable = MetaDisposable()
|
||||
|
||||
private let forceTheme: PresentationTheme?
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
|
||||
private var _hasDim: Bool = false
|
||||
|
|
@ -304,17 +305,12 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
|||
return _hasDim
|
||||
}
|
||||
|
||||
public init(context: AccountContext, forceTheme: PresentationTheme?, send: @escaping (Message) -> Void, updateActivity: @escaping (Bool) -> Void) {
|
||||
public init(context: AccountContext, mode: AttachmentFileControllerMode, presentationData: PresentationData, send: @escaping (Message) -> Void, updateActivity: @escaping (Bool) -> Void) {
|
||||
self.context = context
|
||||
self.send = send
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.forceTheme = forceTheme
|
||||
if let forceTheme = self.forceTheme {
|
||||
self.presentationData = self.presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
|
|
@ -380,13 +376,26 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
|||
return .single(nil)
|
||||
}
|
||||
|
||||
let signal: Signal<[Message]?, NoError> = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .sentMedia(tags: [.file]), query: query, state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
let signal: Signal<[Message]?, NoError>
|
||||
switch mode {
|
||||
case .recent:
|
||||
signal = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .sentMedia(tags: [.file]), query: query, state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
case .audio:
|
||||
signal = .single(nil)
|
||||
|> then(
|
||||
context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil), query: query, state: nil)
|
||||
|> map { result -> [Message]? in
|
||||
return result.0.messages
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
updateActivity(true)
|
||||
|
||||
return combineLatest(signal, presentationDataPromise.get())
|
||||
|
|
@ -420,25 +429,19 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
|||
}
|
||||
}))
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
var presentationData = presentationData
|
||||
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
if let forceTheme = strongSelf.forceTheme {
|
||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
}
|
||||
}
|
||||
})
|
||||
// self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
// |> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
|
||||
// if let strongSelf = self {
|
||||
// let previousTheme = strongSelf.presentationData.theme
|
||||
// let previousStrings = strongSelf.presentationData.strings
|
||||
//
|
||||
// strongSelf.presentationData = presentationData
|
||||
//
|
||||
// if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
// strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
self.listNode.beganInteractiveDragging = { [weak self] _ in
|
||||
self?.dismissInput?()
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageFactCheckBubbleContentNode",
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ import ChatMessageStoryMentionContentNode
|
|||
import ChatMessageUnsupportedBubbleContentNode
|
||||
import ChatMessageWallpaperBubbleContentNode
|
||||
import ChatMessageGiftBubbleContentNode
|
||||
import ChatMessageGiftOfferBubbleContentNode
|
||||
import ChatMessageGiveawayBubbleContentNode
|
||||
import ChatMessageJoinedChannelBubbleContentNode
|
||||
import ChatMessageFactCheckBubbleContentNode
|
||||
|
|
@ -246,6 +247,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||
result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
} else if case .suggestedBirthday = action.action {
|
||||
result.append((message, ChatMessageBirthdateSuggestionContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
} else if case .starGiftPurchaseOffer = action.action {
|
||||
result.append((message, ChatMessageGiftOfferBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
} else {
|
||||
if !canAddMessageReactions(message: message) {
|
||||
needReactions = false
|
||||
|
|
@ -2821,6 +2824,48 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
actionButtonsFinalize = buttonsLayout
|
||||
|
||||
lastNodeTopPosition = .None(.Both)
|
||||
} else if incoming, let action = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGiftPurchaseOffer(_, _, expireDate, isAccepted, isDeclined) = action.action, !isAccepted && !isDeclined {
|
||||
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
if expireDate <= currentTimestamp {
|
||||
|
||||
} else {
|
||||
var buttonDeclineValue: UInt8 = 0
|
||||
let buttonDecline = MemoryBuffer(data: Data(bytes: &buttonDeclineValue, count: 1))
|
||||
var buttonApproveValue: UInt8 = 1
|
||||
let buttonApprove = MemoryBuffer(data: Data(bytes: &buttonApproveValue, count: 1))
|
||||
|
||||
let customInfos: [MemoryBuffer: ChatMessageActionButtonsNode.CustomInfo] = [
|
||||
buttonDecline: ChatMessageActionButtonsNode.CustomInfo(
|
||||
isEnabled: true,
|
||||
icon: .suggestedPostReject
|
||||
),
|
||||
buttonApprove: ChatMessageActionButtonsNode.CustomInfo(
|
||||
isEnabled: true,
|
||||
icon: .suggestedPostApprove
|
||||
)
|
||||
]
|
||||
//TODO:localize
|
||||
let (minWidth, buttonsLayout) = actionButtonsLayout(
|
||||
item.context,
|
||||
item.presentationData.theme,
|
||||
item.presentationData.chatBubbleCorners,
|
||||
item.presentationData.strings,
|
||||
item.controllerInteraction.presentationContext.backgroundNode,
|
||||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: "Reject", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)),
|
||||
ReplyMarkupButton(title: "Accept", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove))
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
placeholder: nil
|
||||
), customInfos, item.message, baseWidth)
|
||||
maxContentWidth = max(maxContentWidth, minWidth)
|
||||
actionButtonsFinalize = buttonsLayout
|
||||
|
||||
lastNodeTopPosition = .None(.Both)
|
||||
}
|
||||
} else if incoming, let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil {
|
||||
var canApprove = true
|
||||
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId, let mainChannel = item.message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect), !mainChannel.hasPermission(.sendSomething) {
|
||||
|
|
@ -4772,7 +4817,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
|
||||
let actionButtonsFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + (incoming ? layoutConstants.bubble.contentInsets.left : layoutConstants.bubble.contentInsets.right), y: backgroundFrame.maxY), size: actionButtonsSizeAndApply.0)
|
||||
var actionButtonsOriginX = backgroundFrame.minX
|
||||
if case .center = alignment {
|
||||
actionButtonsOriginX += 3.0
|
||||
} else {
|
||||
actionButtonsOriginX += incoming ? layoutConstants.bubble.contentInsets.left : layoutConstants.bubble.contentInsets.right
|
||||
}
|
||||
let actionButtonsFrame = CGRect(origin: CGPoint(x: actionButtonsOriginX, y: backgroundFrame.maxY), size: actionButtonsSizeAndApply.0)
|
||||
if actionButtonsNode !== strongSelf.actionButtonsNode {
|
||||
strongSelf.actionButtonsNode = actionButtonsNode
|
||||
actionButtonsNode.buttonPressed = { [weak strongSelf] button, progress in
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
for media in item.message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
releasedBy = gift.releasedBy
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
releasedBy = gift.releasedBy
|
||||
|
|
@ -430,6 +430,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
var buttonTitle = item.presentationData.strings.Notification_PremiumGift_View
|
||||
var buttonIcon: String?
|
||||
var ribbonTitle = ""
|
||||
var customRibbonColors: [UIColor]?
|
||||
var textSpacing: CGFloat = 0.0
|
||||
var isStarGift = false
|
||||
|
||||
|
|
@ -554,7 +555,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
||||
hasServiceMessage = false
|
||||
}
|
||||
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 convertStars = convertStars
|
||||
if case let .generic(gift) = gift {
|
||||
|
|
@ -686,7 +687,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _, _, _, _, fromOffer):
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
isStarGift = true
|
||||
|
||||
|
|
@ -715,7 +716,13 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
|
||||
}
|
||||
text = isStoryEntity ? "**\(item.presentationData.strings.Notification_StarGift_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: item.presentationData.dateTimeFormat))**" : "**\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: item.presentationData.dateTimeFormat))**"
|
||||
ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift
|
||||
if fromOffer {
|
||||
//TODO:localize
|
||||
ribbonTitle = incoming ? "" : "sold"
|
||||
customRibbonColors = [UIColor(rgb: 0xd9433a), UIColor(rgb: 0xff645b)]
|
||||
} else {
|
||||
ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift
|
||||
}
|
||||
buttonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_View
|
||||
modelTitle = item.presentationData.strings.Notification_StarGift_Model
|
||||
backdropTitle = item.presentationData.strings.Notification_StarGift_Backdrop
|
||||
|
|
@ -1285,7 +1292,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
|
||||
if ribbonTextLayout.size.width > 0.0 {
|
||||
if strongSelf.ribbonBackgroundNode.image == nil {
|
||||
if let uniqueBackgroundColor {
|
||||
if let customRibbonColors {
|
||||
strongSelf.ribbonBackgroundNode.image = generateGradientTintedImage(image: UIImage(bundleImageName: "Premium/GiftRibbon"), colors: customRibbonColors, direction: .mirroredDiagonal)
|
||||
} else if let uniqueBackgroundColor {
|
||||
let colors = [
|
||||
uniqueBackgroundColor.withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89),
|
||||
uniqueBackgroundColor.withMultiplied(hue: 1.01, saturation: 1.22, brightness: 1.04)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatMessageGiftOfferBubbleContentNode",
|
||||
module_name = "ChatMessageGiftOfferBubbleContentNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/LocalizedPeerData",
|
||||
"//submodules/UrlEscaping",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/ReactionSelectionNode",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TextFormat
|
||||
import LocalizedPeerData
|
||||
import UrlEscaping
|
||||
import TelegramStringFormatting
|
||||
import WallpaperBackgroundNode
|
||||
import ReactionSelectionNode
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import ChatControllerInteraction
|
||||
import ShimmerEffect
|
||||
import Markdown
|
||||
import ChatMessageBubbleContentNode
|
||||
import ChatMessageItemCommon
|
||||
import TextNodeWithEntities
|
||||
import GiftItemComponent
|
||||
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? {
|
||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true)
|
||||
}
|
||||
|
||||
public class ChatMessageGiftOfferBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var mediaBackgroundContent: WallpaperBubbleBackgroundNode?
|
||||
private let titleNode: TextNode
|
||||
private let subtitleNode: TextNodeWithEntities
|
||||
private let giftIcon = ComponentView<Empty>()
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
|
||||
private var isPlaying: Bool = false
|
||||
|
||||
override public var disablesClipping: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override public var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
let wasVisible = oldValue != .none
|
||||
let isVisible = self.visibility != .none
|
||||
|
||||
if wasVisible != isVisible {
|
||||
self.visibilityStatus = isVisible
|
||||
|
||||
switch self.visibility {
|
||||
case .none:
|
||||
self.subtitleNode.visibilityRect = nil
|
||||
case let .visible(_, subRect):
|
||||
var subRect = subRect
|
||||
subRect.origin.x = 0.0
|
||||
subRect.size.width = 10000.0
|
||||
self.subtitleNode.visibilityRect = subRect
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var visibilityStatus: Bool? {
|
||||
didSet {
|
||||
if self.visibilityStatus != oldValue {
|
||||
self.updateVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var fetchDisposable: Disposable?
|
||||
private var setupTimestamp: Double?
|
||||
|
||||
private var cachedTonImage: (UIImage, UIColor)?
|
||||
|
||||
required public init() {
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
||||
self.subtitleNode = TextNodeWithEntities()
|
||||
self.subtitleNode.textNode.isUserInteractionEnabled = false
|
||||
self.subtitleNode.textNode.displaysAsynchronously = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.subtitleNode.textNode)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.fetchDisposable?.dispose()
|
||||
}
|
||||
|
||||
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeSubtitleLayout = TextNodeWithEntities.asyncLayout(self.subtitleNode)
|
||||
|
||||
return { item, layoutConstants, _, _, _, _ in
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
||||
|
||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||
var giftSize = CGSize(width: 260.0, height: 240.0)
|
||||
var uniqueGift: StarGift.UniqueGift?
|
||||
|
||||
let incoming: Bool
|
||||
if item.message.id.peerId == item.context.account.peerId && item.message.forwardInfo == nil {
|
||||
incoming = true
|
||||
} else {
|
||||
incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
}
|
||||
|
||||
let textColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText
|
||||
|
||||
let text: String
|
||||
let additionalText: String
|
||||
|
||||
var hasActionButtons = false
|
||||
if let action = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGiftPurchaseOffer(gift, amount, expireDate, isAccepted, isDeclined) = action.action {
|
||||
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = "\(amount.amount) Stars"
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
let giftTitle: String
|
||||
if case let .unique(gift) = gift {
|
||||
giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
uniqueGift = gift
|
||||
} else {
|
||||
giftTitle = ""
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
if incoming {
|
||||
text = "**\(peerName)** offered you **\(priceString)** for your gift **\(giftTitle)**."
|
||||
} else {
|
||||
text = "You offered **\(peerName)** **\(priceString)** for their gift **\(giftTitle)**."
|
||||
}
|
||||
|
||||
if isAccepted {
|
||||
additionalText = "This offer was accepted."
|
||||
} else if isDeclined {
|
||||
additionalText = "This offer was rejected."
|
||||
} else if expireDate > currentTimestamp {
|
||||
func textForTimeout(_ value: Int32) -> String {
|
||||
if value < 3600 {
|
||||
let minutes = value / 60
|
||||
//let seconds = value % 60
|
||||
//let secondsPadding = seconds < 10 ? "0" : ""
|
||||
return "\(minutes)m" //\(secondsPadding)\(seconds)"
|
||||
} else {
|
||||
let hours = value / 3600
|
||||
let minutes = (value % 3600) / 60
|
||||
let minutesPadding = minutes < 10 ? "0" : ""
|
||||
//let seconds = value % 60
|
||||
//let secondsPadding = seconds < 10 ? "0" : ""
|
||||
return "\(hours)h \(minutesPadding)\(minutes)m" //:\(secondsPadding)\(seconds)"
|
||||
}
|
||||
}
|
||||
let delta = expireDate - currentTimestamp
|
||||
additionalText = "This offer expires in \(textForTimeout(delta))."
|
||||
|
||||
if incoming {
|
||||
hasActionButtons = true
|
||||
}
|
||||
} else {
|
||||
additionalText = "This offer has expired."
|
||||
}
|
||||
} else {
|
||||
text = ""
|
||||
additionalText = ""
|
||||
}
|
||||
|
||||
let titleAttributedString = NSMutableAttributedString(attributedString: NSAttributedString(string: additionalText, font: Font.regular(13.0), textColor: textColor, paragraphAlignment: .center))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
|
||||
let textConstrainedSize = CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude)
|
||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
giftSize.height = titleLayout.size.height + subtitleLayout.size.height + 162.0
|
||||
|
||||
let backgroundSize = CGSize(width: giftSize.width, height: giftSize.height + 4.0)
|
||||
|
||||
return (backgroundSize.width, { boundingWidth in
|
||||
return (backgroundSize, { [weak self] animation, synchronousLoads, info in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - giftSize.width) / 2.0), y: 0.0), size: giftSize)
|
||||
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||
|
||||
strongSelf.updateVisibility()
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = subtitleApply(TextNodeWithEntities.Arguments(
|
||||
context: item.context,
|
||||
cache: item.controllerInteraction.presentationContext.animationCache,
|
||||
renderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||
placeholderColor: item.presentationData.theme.theme.chat.message.freeform.withWallpaper.reactionInactiveBackground,
|
||||
attemptSynchronous: synchronousLoads
|
||||
))
|
||||
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: mediaBackgroundFrame.minY + 126.0), size: subtitleLayout.size)
|
||||
strongSelf.subtitleNode.textNode.frame = textFrame
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: textFrame.maxY + 23.0), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
||||
if strongSelf.mediaBackgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
backgroundContent.clipsToBounds = true
|
||||
backgroundContent.cornerRadius = 24.0
|
||||
|
||||
strongSelf.mediaBackgroundContent = backgroundContent
|
||||
strongSelf.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
|
||||
if let backgroundContent = strongSelf.mediaBackgroundContent {
|
||||
animation.animator.updateFrame(layer: backgroundContent.layer, frame: mediaBackgroundFrame, completion: nil)
|
||||
backgroundContent.clipsToBounds = true
|
||||
|
||||
if hasActionButtons {
|
||||
backgroundContent.cornerRadius = 0.0
|
||||
if backgroundContent.view.mask == nil {
|
||||
backgroundContent.view.mask = UIImageView(image: generateImage(mediaBackgroundFrame.size, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
|
||||
context.addPath(CGPath(roundedRect: CGRect(x: 0, y: 0, width: size.width, height: size.height * 0.5), cornerWidth: 24.0, cornerHeight: 24.0, transform: nil))
|
||||
context.addPath(CGPath(roundedRect: CGRect(x: 0, y: size.height * 0.5 - 30.0, width: size.width, height: size.height * 0.5 + 30.0), cornerWidth: 8.0, cornerHeight: 8.0, transform: nil))
|
||||
context.fillPath()
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
backgroundContent.view.mask = nil
|
||||
backgroundContent.cornerRadius = 24.0
|
||||
}
|
||||
}
|
||||
|
||||
if let uniqueGift {
|
||||
let iconSize = CGSize(width: 94.0, height: 94.0)
|
||||
let _ = strongSelf.giftIcon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(GiftItemComponent(
|
||||
context: item.context,
|
||||
theme: item.presentationData.theme.theme,
|
||||
strings: item.presentationData.strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: uniqueGift, price: nil),
|
||||
mode: .thumbnail
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: iconSize
|
||||
)
|
||||
if let giftIconView = strongSelf.giftIcon.view {
|
||||
if giftIconView.superview == nil {
|
||||
// backgroundView.layer.cornerRadius = 20.0
|
||||
//backgroundView.clipsToBounds = true
|
||||
strongSelf.view.addSubview(giftIconView)
|
||||
}
|
||||
giftIconView.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY + 17.0), size: iconSize)
|
||||
}
|
||||
}
|
||||
|
||||
if let (rect, size) = strongSelf.absoluteRect {
|
||||
strongSelf.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
switch strongSelf.visibility {
|
||||
case .none:
|
||||
strongSelf.subtitleNode.visibilityRect = nil
|
||||
case let .visible(_, subRect):
|
||||
var subRect = subRect
|
||||
subRect.origin.x = 0.0
|
||||
subRect.size.width = 10000.0
|
||||
strongSelf.subtitleNode.visibilityRect = subRect
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
if let mediaBackgroundContent = self.mediaBackgroundContent {
|
||||
var backgroundFrame = mediaBackgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
mediaBackgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
|
||||
}
|
||||
|
||||
override public func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
|
||||
}
|
||||
|
||||
override public func unreadMessageRangeUpdated() {
|
||||
self.updateVisibility()
|
||||
}
|
||||
|
||||
override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||
if self.mediaBackgroundContent?.frame.contains(point) == true {
|
||||
return ChatMessageBubbleContentTapAction(content: .openMessage)
|
||||
} else {
|
||||
return ChatMessageBubbleContentTapAction(content: .none)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibility() {
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatHistoryEntry",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||
"//submodules/TelegramUI/Components/Chat/ShimmeringLinkNode",
|
||||
"//submodules/AnimatedCountLabelNode",
|
||||
"//submodules/AudioWaveform",
|
||||
"//submodules/DeviceProximity",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import TelegramStringFormatting
|
|||
import AnimatedCountLabelNode
|
||||
import AudioWaveform
|
||||
import DeviceProximity
|
||||
import ShimmeringLinkNode
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
|
|
@ -123,6 +124,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||
public let fetchingTextNode: ImmediateTextNode
|
||||
public let fetchingCompactTextNode: ImmediateTextNode
|
||||
private let countNode: ImmediateAnimatedCountLabelNode
|
||||
private var shimmeringNodes: [ShimmeringLinkNode] = []
|
||||
|
||||
public var waveformView: ComponentHostView<Empty>?
|
||||
|
||||
|
|
@ -809,23 +811,28 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||
|
||||
var displayTrailingAnimatedDots = false
|
||||
|
||||
var isTranslating = false
|
||||
if let transcribedText = transcribedText, case .expanded = effectiveAudioTranscriptionState {
|
||||
switch transcribedText {
|
||||
case let .success(text, isPending):
|
||||
textString = NSAttributedString(string: text, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
|
||||
/*#if DEBUG
|
||||
var isPending = isPending
|
||||
if "".isEmpty {
|
||||
isPending = true
|
||||
}
|
||||
#endif*/
|
||||
|
||||
if isPending {
|
||||
let modifiedString = NSMutableAttributedString(attributedString: textString!)
|
||||
modifiedString.append(NSAttributedString(string: "...", font: textFont, textColor: .clear))
|
||||
displayTrailingAnimatedDots = true
|
||||
textString = modifiedString
|
||||
} else {
|
||||
if let translateToLanguage = arguments.associatedData.translateToLanguage, !text.isEmpty && arguments.incoming {
|
||||
isTranslating = true
|
||||
for attribute in arguments.message.attributes {
|
||||
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
|
||||
textString = NSAttributedString(string: attribute.text, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
isTranslating = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .error(error):
|
||||
let errorTextFont = Font.regular(floor(arguments.presentationData.fontSize.baseDisplaySize * 15.0 / 17.0))
|
||||
|
|
@ -1538,6 +1545,8 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||
} else {
|
||||
strongSelf.dateAndStatusNode.pressed = nil
|
||||
}
|
||||
|
||||
strongSelf.updateIsTranslating(isTranslating)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
@ -1545,6 +1554,39 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
private func updateIsTranslating(_ isTranslating: Bool) {
|
||||
guard let arguments = self.arguments else {
|
||||
return
|
||||
}
|
||||
var rects: [[CGRect]] = []
|
||||
let titleRects = (self.textNode.rangeRects(in: NSRange(location: 0, length: self.textNode.cachedLayout?.attributedString?.length ?? 0))?.rects ?? []).map { self.textNode.view.convert($0, to: self.textClippingNode.view) }
|
||||
rects.append(titleRects)
|
||||
|
||||
if isTranslating, !rects.isEmpty {
|
||||
if self.shimmeringNodes.isEmpty {
|
||||
for rects in rects {
|
||||
let shimmeringNode = ShimmeringLinkNode(color: arguments.message.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1))
|
||||
shimmeringNode.updateRects(rects)
|
||||
shimmeringNode.frame = self.bounds
|
||||
shimmeringNode.updateLayout(self.bounds.size)
|
||||
shimmeringNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.shimmeringNodes.append(shimmeringNode)
|
||||
self.textClippingNode.insertSubnode(shimmeringNode, belowSubnode: self.textNode)
|
||||
}
|
||||
}
|
||||
} else if !self.shimmeringNodes.isEmpty {
|
||||
let shimmeringNodes = self.shimmeringNodes
|
||||
self.shimmeringNodes = []
|
||||
|
||||
for shimmeringNode in shimmeringNodes {
|
||||
shimmeringNode.alpha = 0.0
|
||||
shimmeringNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak shimmeringNode] _ in
|
||||
shimmeringNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStatus(animated: Bool) {
|
||||
guard let resourceStatus = self.resourceStatus else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -490,19 +490,29 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
|||
actionTitle = item.presentationData.strings.Chat_ViewAlbum
|
||||
case "telegram_auction":
|
||||
var hasEnded = false
|
||||
var isUpcoming = false
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
for attribute in webpage.attributes {
|
||||
if case let .giftAuction(giftAuction) = attribute {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if case let .generic(gift) = giftAuction.gift, let auctionStartDate = gift.auctionStartDate, currentTime < auctionStartDate {
|
||||
isUpcoming = true
|
||||
}
|
||||
if giftAuction.endDate < currentTime {
|
||||
hasEnded = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
subtitle = NSAttributedString(string: item.presentationData.strings.Chat_Auction, font: titleFont)
|
||||
text = nil
|
||||
actionTitle = hasEnded ? item.presentationData.strings.Chat_Auction_ViewResults : item.presentationData.strings.Chat_Auction_Join
|
||||
actionIcon = !hasEnded ? .bid : nil
|
||||
if isUpcoming {
|
||||
subtitle = NSAttributedString(string: item.presentationData.strings.Chat_Auction_Upcoming, font: titleFont)
|
||||
actionTitle = item.presentationData.strings.Chat_Auction_View
|
||||
actionIcon = nil
|
||||
} else {
|
||||
subtitle = NSAttributedString(string: item.presentationData.strings.Chat_Auction, font: titleFont)
|
||||
actionTitle = hasEnded ? item.presentationData.strings.Chat_Auction_ViewResults : item.presentationData.strings.Chat_Auction_Join
|
||||
actionIcon = !hasEnded ? .bid : nil
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ swift_library(
|
|||
"//submodules/AnimatedCountLabelNode",
|
||||
"//submodules/HexColor",
|
||||
"//submodules/QrCodeUI",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/SheetComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ import SegmentedControlNode
|
|||
import AnimatedCountLabelNode
|
||||
import HexColor
|
||||
import QrCodeUI
|
||||
import ComponentFlow
|
||||
import GlassBarButtonComponent
|
||||
import SheetComponent
|
||||
import BundleIconComponent
|
||||
|
||||
private func closeButtonImage(theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
|
|
@ -717,7 +721,7 @@ public final class ChatQrCodeScreenImpl: ViewController, ChatQrCodeScreen {
|
|||
}
|
||||
|
||||
private func iconColors(theme: PresentationTheme) -> [String: UIColor] {
|
||||
let accentColor = theme.actionSheet.controlAccentColor
|
||||
let accentColor = theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
var colors: [String: UIColor] = [:]
|
||||
colors["Sunny.Path 14.Path.Stroke 1"] = accentColor
|
||||
colors["Sunny.Path 15.Path.Stroke 1"] = accentColor
|
||||
|
|
@ -745,18 +749,6 @@ private func interpolateColors(from: [String: UIColor], to: [String: UIColor], f
|
|||
|
||||
private let defaultEmoticon = "🏠"
|
||||
|
||||
private func generateShadowImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: -0.5), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
|
||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
|
||||
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: CGSize(width: 40.0, height: 40.0)), cornerRadius: 16.0)
|
||||
context.addPath(path.cgPath)
|
||||
context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: 20, topCapHeight: 0)
|
||||
}
|
||||
|
||||
private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
|
@ -768,14 +760,14 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
private let scrollNodeContentNode: ASDisplayNode
|
||||
private let contentContainerNode: ASDisplayNode
|
||||
private let topContentContainerNode: SparseNode
|
||||
private let shadowNode: ASImageNode
|
||||
private let effectNode: ASDisplayNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let contentBackgroundNode: ASDisplayNode
|
||||
private let contentBackgroundView: SheetBackgroundView
|
||||
private let cancelButton = ComponentView<Empty>()
|
||||
private let switchThemeButton = ComponentView<Empty>()
|
||||
private let titleNode: ASTextNode
|
||||
private let segmentedNode: SegmentedControlNode
|
||||
private let cancelButton: HighlightableButtonNode
|
||||
private let switchThemeButton: HighlightTrackingButtonNode
|
||||
private let cancelButtonNode: HighlightableButtonNode
|
||||
private let switchThemeButtonNode: HighlightTrackingButtonNode
|
||||
private let animationContainerNode: ASDisplayNode
|
||||
private var animationNode: AnimationNode
|
||||
private let doneButton: SolidRoundedButtonNode
|
||||
|
|
@ -841,28 +833,17 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
self.topContentContainerNode = SparseNode()
|
||||
self.topContentContainerNode.isOpaque = false
|
||||
|
||||
self.shadowNode = ASImageNode()
|
||||
self.shadowNode.contentMode = .scaleToFill
|
||||
self.shadowNode.image = generateShadowImage()
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.clipsToBounds = true
|
||||
self.backgroundNode.cornerRadius = 16.0
|
||||
self.backgroundNode.cornerRadius = 38.0
|
||||
|
||||
self.isDarkAppearance = self.presentationData.theme.overallDarkAppearance
|
||||
self.isDarkAppearancePromise = ValuePromise(self.presentationData.theme.overallDarkAppearance)
|
||||
|
||||
let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
||||
let textColor = self.presentationData.theme.actionSheet.primaryTextColor
|
||||
let blurStyle: UIBlurEffect.Style = self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark
|
||||
|
||||
self.effectNode = ASDisplayNode(viewBlock: {
|
||||
return UIVisualEffectView(effect: UIBlurEffect(style: blurStyle))
|
||||
})
|
||||
|
||||
self.contentBackgroundNode = ASDisplayNode()
|
||||
self.contentBackgroundNode.backgroundColor = backgroundColor
|
||||
|
||||
self.contentBackgroundView = SheetBackgroundView()
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
let title: String
|
||||
|
|
@ -878,12 +859,12 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
self.segmentedNode.isHidden = !self.contentNode.hasVideo
|
||||
self.titleNode.isHidden = !self.segmentedNode.isHidden
|
||||
|
||||
self.cancelButton = HighlightableButtonNode()
|
||||
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||
self.cancelButton.accessibilityLabel = self.presentationData.strings.Common_Close
|
||||
self.cancelButton.accessibilityTraits = [.button]
|
||||
self.cancelButtonNode = HighlightableButtonNode()
|
||||
self.cancelButtonNode.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||
self.cancelButtonNode.accessibilityLabel = self.presentationData.strings.Common_Close
|
||||
self.cancelButtonNode.accessibilityTraits = [.button]
|
||||
|
||||
self.switchThemeButton = HighlightTrackingButtonNode()
|
||||
self.switchThemeButtonNode = HighlightTrackingButtonNode()
|
||||
self.animationContainerNode = ASDisplayNode()
|
||||
self.animationContainerNode.isUserInteractionEnabled = false
|
||||
|
||||
|
|
@ -916,13 +897,10 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
self.scrollNodeContentNode.addSubnode(self.contentNode)
|
||||
|
||||
self.scrollNodeContentNode.addSubnode(self.shadowNode)
|
||||
self.scrollNodeContentNode.addSubnode(self.backgroundNode)
|
||||
self.scrollNodeContentNode.addSubnode(self.contentContainerNode)
|
||||
self.scrollNodeContentNode.addSubnode(self.topContentContainerNode)
|
||||
|
||||
self.backgroundNode.addSubnode(self.effectNode)
|
||||
self.backgroundNode.addSubnode(self.contentBackgroundNode)
|
||||
self.contentContainerNode.addSubnode(self.titleNode)
|
||||
self.contentContainerNode.addSubnode(self.segmentedNode)
|
||||
self.contentContainerNode.addSubnode(self.doneButton)
|
||||
|
|
@ -930,12 +908,12 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
self.topContentContainerNode.addSubnode(self.animationContainerNode)
|
||||
self.animationContainerNode.addSubnode(self.animationNode)
|
||||
self.topContentContainerNode.addSubnode(self.switchThemeButton)
|
||||
//self.topContentContainerNode.addSubnode(self.switchThemeButtonNode)
|
||||
self.topContentContainerNode.addSubnode(self.listNode)
|
||||
self.topContentContainerNode.addSubnode(self.cancelButton)
|
||||
//self.topContentContainerNode.addSubnode(self.cancelButtonNode)
|
||||
|
||||
self.switchThemeButton.addTarget(self, action: #selector(self.switchThemePressed), forControlEvents: .touchUpInside)
|
||||
self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.switchThemeButtonNode.addTarget(self, action: #selector(self.switchThemePressed), forControlEvents: .touchUpInside)
|
||||
self.cancelButtonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.segmentedNode.selectedIndexChanged = { [weak self] index in
|
||||
guard let strongSelf = self, let contentNode = strongSelf.contentNode as? MessageContentNode, let videoNode = contentNode.videoNode else {
|
||||
|
|
@ -1181,7 +1159,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
}
|
||||
}))
|
||||
|
||||
self.switchThemeButton.highligthedChanged = { [weak self] highlighted in
|
||||
self.switchThemeButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.animationContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||
|
|
@ -1280,14 +1258,14 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
}
|
||||
|
||||
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||
self.cancelButtonNode.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||
self.doneButton.updateTheme(SolidRoundedButtonTheme(theme: self.presentationData.theme))
|
||||
self.scanButton.updateTheme(SolidRoundedButtonTheme(backgroundColor: .clear, foregroundColor: self.presentationData.theme.actionSheet.controlAccentColor))
|
||||
|
||||
let previousIconColors = iconColors(theme: previousTheme)
|
||||
let newIconColors = iconColors(theme: self.presentationData.theme)
|
||||
|
||||
if !self.switchThemeButton.isUserInteractionEnabled {
|
||||
if !self.switchThemeButtonNode.isUserInteractionEnabled {
|
||||
let themeCrossfadeDuration: Double = 0.3
|
||||
let themeCrossfadeDelay: Double = 0.25
|
||||
|
||||
|
|
@ -1312,6 +1290,8 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
self.backgroundNode.view.addSubview(self.contentBackgroundView)
|
||||
|
||||
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
|
||||
|
|
@ -1320,9 +1300,9 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
}
|
||||
|
||||
@objc private func switchThemePressed() {
|
||||
self.switchThemeButton.isUserInteractionEnabled = false
|
||||
self.switchThemeButtonNode.isUserInteractionEnabled = false
|
||||
Queue.mainQueue().after(0.5) {
|
||||
self.switchThemeButton.isUserInteractionEnabled = true
|
||||
self.switchThemeButtonNode.isUserInteractionEnabled = true
|
||||
}
|
||||
|
||||
self.animateCrossfade(animateIcon: false)
|
||||
|
|
@ -1372,18 +1352,11 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
})
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(ChatQrCodeScreenImpl.themeCrossfadeDelay) {
|
||||
if let effectView = self.effectNode.view as? UIVisualEffectView {
|
||||
UIView.animate(withDuration: ChatQrCodeScreenImpl.themeCrossfadeDuration, delay: 0.0, options: .curveLinear) {
|
||||
effectView.effect = UIBlurEffect(style: self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark)
|
||||
} completion: { _ in
|
||||
}
|
||||
}
|
||||
|
||||
let previousColor = self.contentBackgroundNode.backgroundColor ?? .clear
|
||||
self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
||||
self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: ChatQrCodeScreenImpl.themeCrossfadeDuration)
|
||||
}
|
||||
// Queue.mainQueue().after(ChatQrCodeScreenImpl.themeCrossfadeDelay) {
|
||||
// let previousColor = self.contentBackgroundNode.backgroundColor ?? .clear
|
||||
// self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
|
||||
// self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: ChatQrCodeScreenImpl.themeCrossfadeDuration)
|
||||
// }
|
||||
|
||||
if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = self.contentContainerNode.frame
|
||||
|
|
@ -1403,7 +1376,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
private var animatedOut = false
|
||||
public func animateIn() {
|
||||
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
|
||||
let offset = self.bounds.size.height - self.contentBackgroundView.frame.minY
|
||||
|
||||
if let (layout, _) = self.containerLayout {
|
||||
self.scrollNodeContentNode.cornerRadius = layout.deviceMetrics.screenCornerRadius
|
||||
|
|
@ -1424,7 +1397,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
self.wrappingScrollNode.view.isScrollEnabled = false
|
||||
|
||||
let distance = self.bounds.size.height - self.contentBackgroundNode.frame.minY
|
||||
let distance = self.bounds.size.height - self.contentBackgroundView.frame.minY
|
||||
if let velocity {
|
||||
let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity / distance)
|
||||
self.wrappingScrollNode.layer.animateSpring(from: 0.0 as NSNumber, to: -distance as NSNumber, keyPath: "bounds.origin.y", duration: 0.45, delay: 0.0, initialVelocity: initialVelocity, damping: 124.0, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
|
|
@ -1473,24 +1446,81 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
||||
|
||||
let sideInset = floor((layout.size.width - width) / 2.0)
|
||||
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight))
|
||||
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight - 6.0), size: CGSize(width: width, height: contentHeight)).insetBy(dx: 6.0, dy: 0.0)
|
||||
let contentFrame = contentContainerFrame
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0))
|
||||
if backgroundFrame.minY < contentFrame.minY {
|
||||
backgroundFrame.origin.y = contentFrame.minY
|
||||
}
|
||||
|
||||
let shadowFrame = CGRect(x: backgroundFrame.minX, y: backgroundFrame.minY - 8.0, width: backgroundFrame.width, height: 40.0)
|
||||
transition.updateFrame(node: self.shadowNode, frame: shadowFrame)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||
transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
|
||||
self.contentBackgroundView.update(size: contentFrame.size, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor, topCornerRadius: 38.0, bottomCornerRadius: layout.deviceMetrics.screenCornerRadius - 2.0, transition: .immediate)
|
||||
transition.updateFrame(view: self.contentBackgroundView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
|
||||
let barButtonSize = CGSize(width: 40.0, height: 40.0)
|
||||
let cancelButtonSize = self.cancelButton.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: barButtonSize,
|
||||
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: self.presentationData.theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
self?.cancelButtonPressed()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: barButtonSize
|
||||
)
|
||||
let cancelButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: cancelButtonSize)
|
||||
if let view = self.cancelButton.view {
|
||||
if view.superview == nil {
|
||||
self.topContentContainerNode.view.addSubview(view)
|
||||
}
|
||||
view.bounds = CGRect(origin: .zero, size: cancelButtonFrame.size)
|
||||
view.center = cancelButtonFrame.center
|
||||
}
|
||||
|
||||
let switchThemeButtonSize = self.switchThemeButton.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: barButtonSize,
|
||||
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: self.presentationData.theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "switchTheme", component: AnyComponent(
|
||||
Rectangle(color: .clear)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
self?.switchThemePressed()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: barButtonSize
|
||||
)
|
||||
let switchThemeButtonFrame = CGRect(origin: CGPoint(x: contentFrame.width - switchThemeButtonSize.width - 16.0, y: 16.0), size: switchThemeButtonSize)
|
||||
if let view = self.switchThemeButton.view {
|
||||
if view.superview == nil {
|
||||
self.topContentContainerNode.view.addSubview(view)
|
||||
self.topContentContainerNode.view.bringSubviewToFront(self.animationContainerNode.view)
|
||||
}
|
||||
view.bounds = CGRect(origin: .zero, size: switchThemeButtonFrame.size)
|
||||
view.center = switchThemeButtonFrame.center
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.scrollNodeContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height + 2000.0)))
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: width - 90.0, height: titleHeight))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 19.0 + UIScreenPixel), size: titleSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 36.0 - titleSize.height / 2.0), size: titleSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
|
||||
let segmentedSize = self.segmentedNode.updateLayout(.sizeToFit(maximumWidth: width - 90.0, minimumWidth: 160.0, height: 32.0), transition: transition)
|
||||
|
|
@ -1498,20 +1528,20 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
|
||||
let switchThemeSize = CGSize(width: 44.0, height: 44.0)
|
||||
let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize)
|
||||
transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame)
|
||||
transition.updateFrame(node: self.animationContainerNode, frame: switchThemeFrame.insetBy(dx: 9.0, dy: 9.0))
|
||||
transition.updateFrame(node: self.switchThemeButtonNode, frame: switchThemeFrame)
|
||||
transition.updateFrame(node: self.animationContainerNode, frame: switchThemeButtonFrame.insetBy(dx: 5.0, dy: 5.0))
|
||||
transition.updateFrameAsPositionAndBounds(node: self.animationNode, frame: CGRect(origin: CGPoint(), size: self.animationContainerNode.frame.size))
|
||||
|
||||
let cancelSize = CGSize(width: 44.0, height: 44.0)
|
||||
let cancelFrame = CGRect(origin: CGPoint(x: contentFrame.width - cancelSize.width - 3.0, y: 6.0), size: cancelSize)
|
||||
transition.updateFrame(node: self.cancelButton, frame: cancelFrame)
|
||||
transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame)
|
||||
|
||||
let buttonInset: CGFloat = 16.0
|
||||
let buttonInset: CGFloat = 30.0
|
||||
let scanButtonHeight = self.scanButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition)
|
||||
transition.updateFrame(node: self.scanButton, frame: CGRect(x: buttonInset, y: contentHeight - scanButtonHeight - insets.bottom - 6.0, width: contentFrame.width, height: scanButtonHeight))
|
||||
transition.updateFrame(node: self.scanButton, frame: CGRect(x: buttonInset, y: contentHeight - scanButtonHeight - insets.bottom, width: contentFrame.width, height: scanButtonHeight))
|
||||
|
||||
let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition)
|
||||
transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - scanButtonHeight - 10.0 - insets.bottom - 6.0, width: contentFrame.width, height: doneButtonHeight))
|
||||
transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - scanButtonHeight - 10.0 - insets.bottom, width: contentFrame.width, height: doneButtonHeight))
|
||||
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
|
||||
transition.updateFrame(node: self.topContentContainerNode, frame: contentContainerFrame)
|
||||
|
|
@ -1523,7 +1553,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg
|
|||
let contentSize = CGSize(width: contentFrame.width, height: 120.0)
|
||||
|
||||
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: contentSize.height, height: contentSize.width)
|
||||
self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight + 6.0)
|
||||
self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight + 12.0)
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
self.contentNode.updateLayout(size: layout.size, topInset: 44.0, bottomInset: contentHeight, transition: transition)
|
||||
|
|
|
|||
|
|
@ -1327,7 +1327,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||
return
|
||||
}
|
||||
if !self.context.isPremium || count < 3, let pushController = controller.pushController {
|
||||
let controller = self.context.sharedContext.makeGiftWearPreviewScreen(context: self.context, gift: gift)
|
||||
let controller = self.context.sharedContext.makeGiftWearPreviewScreen(context: self.context, gift: .unique(gift), attributes: nil)
|
||||
pushController(controller)
|
||||
} else {
|
||||
let _ = (self.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: nil)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ import UIKitRuntimeUtils
|
|||
public final class GiftCompositionComponent: Component {
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var previewPatternColor: UIColor?
|
||||
public fileprivate(set) var previewModel: StarGift.UniqueGift.Attribute?
|
||||
public fileprivate(set) var previewBackdrop: StarGift.UniqueGift.Attribute?
|
||||
public fileprivate(set) var previewSymbol: StarGift.UniqueGift.Attribute?
|
||||
|
||||
public init() {
|
||||
self.previewPatternColor = nil
|
||||
}
|
||||
|
|
@ -671,18 +675,23 @@ public final class GiftCompositionComponent: Component {
|
|||
loop = true
|
||||
self.stopSpinIfNeeded()
|
||||
|
||||
if self.previewModels.isEmpty {
|
||||
if self.previewModels.isEmpty || sampleAttributes.count == 3 {
|
||||
var models: [StarGift.UniqueGift.Attribute] = []
|
||||
var patterns: [StarGift.UniqueGift.Attribute] = []
|
||||
var backdrops: [StarGift.UniqueGift.Attribute] = []
|
||||
for attribute in sampleAttributes {
|
||||
switch attribute {
|
||||
case .model: models.append(attribute)
|
||||
case .model: models.append(attribute)
|
||||
case .pattern: patterns.append(attribute)
|
||||
case .backdrop: backdrops.append(attribute)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
if self.previewModels != models && sampleAttributes.count == 3 {
|
||||
self.animatePreviewTransition = true
|
||||
}
|
||||
|
||||
self.previewModels = models
|
||||
self.previewPatterns = patterns
|
||||
self.previewBackdrops = backdrops
|
||||
|
|
@ -707,19 +716,22 @@ public final class GiftCompositionComponent: Component {
|
|||
}
|
||||
if case let .model(_, file, _) = self.previewModels[Int(self.previewModelIndex)] {
|
||||
animationFile = file
|
||||
component.externalState?.previewModel = self.previewModels[Int(self.previewModelIndex)]
|
||||
}
|
||||
if case let .pattern(_, file, _) = self.previewPatterns[Int(self.previewPatternIndex)] {
|
||||
patternFile = file
|
||||
files[file.fileId.id] = file
|
||||
component.externalState?.previewSymbol = self.previewPatterns[Int(self.previewPatternIndex)]
|
||||
}
|
||||
if case let .backdrop(_, _, innerColorValue, outerColorValue, patternColorValue, _, _) = self.previewBackdrops[Int(self.previewBackdropIndex)] {
|
||||
backgroundColor = UIColor(rgb: UInt32(bitPattern: outerColorValue))
|
||||
secondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColorValue))
|
||||
patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue))
|
||||
component.externalState?.previewBackdrop = self.previewBackdrops[Int(self.previewBackdropIndex)]
|
||||
}
|
||||
}
|
||||
|
||||
if self.previewTimer == nil {
|
||||
if self.previewTimer == nil && sampleAttributes.count > 3 {
|
||||
self.previewTimer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in
|
||||
guard let self, !self.previewModels.isEmpty else { return }
|
||||
self.previewModelIndex = (self.previewModelIndex + 1) % Int32(self.previewModels.count)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public final class GiftItemComponent: Component {
|
|||
case starGift(gift: StarGift.Gift, price: String)
|
||||
case uniqueGift(gift: StarGift.UniqueGift, price: String?)
|
||||
case auction(gift: StarGift.Gift, centerColor: UIColor, edgeColor: UIColor, endTime: Int32)
|
||||
case preview(attributes: [StarGift.UniqueGift.Attribute], rarity: Int32)
|
||||
}
|
||||
|
||||
public struct Ribbon: Equatable {
|
||||
|
|
@ -144,6 +145,7 @@ public final class GiftItemComponent: Component {
|
|||
case buttonIcon
|
||||
case tableIcon
|
||||
case header
|
||||
case upgradePreview
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
|
|
@ -421,6 +423,14 @@ public final class GiftItemComponent: Component {
|
|||
size = availableSize
|
||||
iconSize = CGSize(width: 106.0, height: 106.0)
|
||||
cornerRadius = 16.0
|
||||
case .upgradePreview:
|
||||
size = availableSize
|
||||
if case let .preview(attributes, _) = component.subject, attributes.count == 2 {
|
||||
iconSize = CGSize(width: 60.0, height: 60.0)
|
||||
} else {
|
||||
iconSize = CGSize(width: 72.0, height: 72.0)
|
||||
}
|
||||
cornerRadius = 16.0
|
||||
}
|
||||
var backgroundSize = size
|
||||
if case .grid = component.mode {
|
||||
|
|
@ -529,7 +539,7 @@ public final class GiftItemComponent: Component {
|
|||
animationFile = gift.file
|
||||
backgroundColor = edgeColor
|
||||
secondBackgroundColor = centerColor
|
||||
|
||||
|
||||
emoji = ChatTextInputTextCustomEmojiAttribute(
|
||||
interactivelySelectedFromPackId: nil,
|
||||
fileId: gift.file.fileId.id,
|
||||
|
|
@ -537,8 +547,14 @@ public final class GiftItemComponent: Component {
|
|||
)
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
let endTimeout = max(0, endTime - currentTime)
|
||||
|
||||
let endTimeout: Int32
|
||||
if let auctionStartDate = gift.auctionStartDate, currentTime < auctionStartDate {
|
||||
endTimeout = max(0, auctionStartDate - currentTime)
|
||||
animatedBadgeItems.append(AnimatedTextComponent.Item(id: "starts", content: .text(component.strings.Chat_Auction_StartsIn)))
|
||||
} else {
|
||||
endTimeout = max(0, endTime - currentTime)
|
||||
}
|
||||
if endTimeout > 0 {
|
||||
let hours = Int(endTimeout / 3600)
|
||||
let minutes = Int((endTimeout % 3600) / 60)
|
||||
|
|
@ -565,6 +581,45 @@ public final class GiftItemComponent: Component {
|
|||
}, queue: Queue.mainQueue())
|
||||
self.giftAuctionTimer?.start()
|
||||
}
|
||||
case let .preview(attributes, _):
|
||||
animationOffset = 16.0
|
||||
explicitAnimationOffset = -4.0
|
||||
for attribute in attributes {
|
||||
switch attribute {
|
||||
case let .model(_, file, _):
|
||||
animationFile = file
|
||||
if !self.fetchedFiles.contains(file.fileId.id) {
|
||||
self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
||||
self.fetchedFiles.insert(file.fileId.id)
|
||||
}
|
||||
case let .pattern(_, file, _):
|
||||
patternFile = file
|
||||
files[file.fileId.id] = file
|
||||
case let .backdrop(_, _, innerColorValue, outerColorValue, patternColorValue, _, _):
|
||||
backgroundColor = UIColor(rgb: UInt32(bitPattern: outerColorValue))
|
||||
secondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColorValue))
|
||||
patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue))
|
||||
if let backgroundColor {
|
||||
placeholderColor = backgroundColor
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if animationFile == nil, let patternFile {
|
||||
animationFile = patternFile
|
||||
}
|
||||
|
||||
if let animationFile {
|
||||
emoji = ChatTextInputTextCustomEmojiAttribute(
|
||||
interactivelySelectedFromPackId: nil,
|
||||
fileId: animationFile.fileId.id,
|
||||
file: animationFile
|
||||
)
|
||||
} else {
|
||||
emoji = nil
|
||||
}
|
||||
}
|
||||
|
||||
if [.buttonIcon, .tableIcon].contains(component.mode) {
|
||||
|
|
@ -640,7 +695,62 @@ public final class GiftItemComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if case .preview = component.mode {
|
||||
if case .upgradePreview = component.mode, case let .preview(attributes, rarity) = component.subject {
|
||||
let isColored = attributes.count > 1
|
||||
if let title = component.title {
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: title, font: Font.medium(13.0), textColor: isColored ? .white : component.theme.list.itemPrimaryTextColor)),
|
||||
horizontalAlignment: .center
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: size.height - 27.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
transition.setFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
}
|
||||
|
||||
func formatPercentage(_ value: Float) -> String {
|
||||
return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%"
|
||||
}
|
||||
let percentage = Float(rarity) * 0.1
|
||||
|
||||
let badgeTextSize = self.badgeText.update(
|
||||
transition: .spring(duration: 0.2),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: formatPercentage(percentage), font: Font.with(size: 11.0, weight: .medium, traits: .monospacedNumbers), textColor: isColored ? .white : component.theme.list.itemSecondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let badgeBackgroundSize = CGSize(width: badgeTextSize.width + 12.0, height: 18.0)
|
||||
let _ = self.badgeBackground.update(
|
||||
transition: .spring(duration: 0.2),
|
||||
component: AnyComponent(
|
||||
RoundedRectangle(color: isColored ? UIColor(white: 0.0, alpha: 0.2) : component.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.06), cornerRadius: 9.0)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: badgeBackgroundSize
|
||||
)
|
||||
|
||||
if let badgeBackgroundView = self.badgeBackground.view, let badgeTextView = self.badgeText.view {
|
||||
if badgeBackgroundView.superview == nil {
|
||||
self.addSubview(badgeBackgroundView)
|
||||
self.addSubview(badgeTextView)
|
||||
}
|
||||
badgeTextView.frame = CGRect(origin: CGPoint(x: size.width - 12.0 - badgeTextSize.width, y: 9.0), size: badgeTextSize)
|
||||
badgeBackgroundView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(badgeTextView.frame.center.x - badgeBackgroundSize.width / 2.0), y: floorToScreenPixels(badgeTextView.frame.center.y - badgeBackgroundSize.height / 2.0)), size: badgeBackgroundSize)
|
||||
}
|
||||
} else if case .preview = component.mode {
|
||||
if let title = component.title {
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
|
|
@ -793,7 +903,9 @@ public final class GiftItemComponent: Component {
|
|||
case .auction:
|
||||
buttonColor = .clear
|
||||
price = ""
|
||||
break
|
||||
case .preview:
|
||||
buttonColor = .clear
|
||||
price = ""
|
||||
}
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
|
|
@ -1189,9 +1301,19 @@ public final class GiftItemComponent: Component {
|
|||
}
|
||||
|
||||
switch component.mode {
|
||||
case .generic, .grid:
|
||||
case .generic, .grid, .upgradePreview:
|
||||
let lineWidth: CGFloat = 2.0
|
||||
let selectionFrame = backgroundFrame.insetBy(dx: 3.0, dy: 3.0)
|
||||
let selectionFrame = backgroundFrame.insetBy(dx: 2.0, dy: 2.0)
|
||||
|
||||
var cornerRadius: CGFloat = 6.0
|
||||
if case .upgradePreview = component.mode {
|
||||
cornerRadius = 13.0
|
||||
}
|
||||
|
||||
var selectionColor = UIColor.white
|
||||
if case .upgradePreview = component.mode, case let .preview(attributes, _) = component.subject, attributes.count == 1 {
|
||||
selectionColor = component.theme.list.itemAccentColor
|
||||
}
|
||||
|
||||
if component.isSelected {
|
||||
let selectionLayer: SimpleShapeLayer
|
||||
|
|
@ -1207,13 +1329,13 @@ public final class GiftItemComponent: Component {
|
|||
}
|
||||
|
||||
selectionLayer.fillColor = UIColor.clear.cgColor
|
||||
selectionLayer.strokeColor = UIColor.white.cgColor
|
||||
selectionLayer.strokeColor = selectionColor.cgColor
|
||||
selectionLayer.lineWidth = lineWidth
|
||||
selectionLayer.frame = selectionFrame
|
||||
selectionLayer.path = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), cornerWidth: 6.0, cornerHeight: 6.0, transform: nil)
|
||||
selectionLayer.path = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
let initialPath = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: 0.0, dy: 0.0), cornerWidth: 6.0, cornerHeight: 6.0, transform: nil)
|
||||
let initialPath = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: 0.0, dy: 0.0), cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
|
||||
selectionLayer.animate(from: initialPath, to: selectionLayer.path as AnyObject, keyPath: "path", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
|
||||
selectionLayer.animateShapeLineWidth(from: 0.0, to: lineWidth, duration: 0.2)
|
||||
}
|
||||
|
|
@ -1222,7 +1344,7 @@ public final class GiftItemComponent: Component {
|
|||
} else if let selectionLayer = self.selectionLayer {
|
||||
self.selectionLayer = nil
|
||||
|
||||
let targetPath = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: 0.0, dy: 0.0), cornerWidth: 6.0, cornerHeight: 6.0, transform: nil)
|
||||
let targetPath = CGPath(roundedRect: CGRect(origin: .zero, size: selectionFrame.size).insetBy(dx: 0.0, dy: 0.0), cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
|
||||
selectionLayer.animate(from: selectionLayer.path, to: targetPath, keyPath: "path", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false)
|
||||
selectionLayer.animateShapeLineWidth(from: selectionLayer.lineWidth, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
selectionLayer.removeFromSuperlayer()
|
||||
|
|
|
|||
|
|
@ -585,6 +585,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||
self.starsItems[itemId] = visibleItem
|
||||
}
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
var ribbon: GiftItemComponent.Ribbon?
|
||||
var outline: GiftItemComponent.Outline?
|
||||
var isSoldOut = false
|
||||
|
|
@ -608,7 +609,11 @@ final class GiftOptionsScreenComponent: Component {
|
|||
let text: String
|
||||
var ribbonColor: GiftItemComponent.Ribbon.Color = .blue
|
||||
if gift.flags.contains(.isAuction) {
|
||||
text = environment.strings.Gift_Options_Gift_Auction
|
||||
if let auctionStartDate = gift.auctionStartDate, currentTime < auctionStartDate {
|
||||
text = environment.strings.Gift_Options_Gift_Soon
|
||||
} else {
|
||||
text = environment.strings.Gift_Options_Gift_Auction
|
||||
}
|
||||
ribbonColor = .orange
|
||||
outline = .orange
|
||||
} else if let perUserLimit = gift.perUserLimit, component.peerId.namespace != Namespaces.Peer.CloudChannel {
|
||||
|
|
@ -624,7 +629,11 @@ final class GiftOptionsScreenComponent: Component {
|
|||
if !isSoldOut && gift.flags.contains(.requiresPremium) {
|
||||
let text: String
|
||||
if gift.flags.contains(.isAuction) {
|
||||
text = environment.strings.Gift_Options_Gift_Auction
|
||||
if let auctionStartDate = gift.auctionStartDate, currentTime < auctionStartDate {
|
||||
text = environment.strings.Gift_Options_Gift_Soon
|
||||
} else {
|
||||
text = environment.strings.Gift_Options_Gift_Auction
|
||||
}
|
||||
} else if component.context.isPremium, let perUserLimit = gift.perUserLimit {
|
||||
text = environment.strings.Gift_Options_Gift_Premium_Left(perUserLimit.remains)
|
||||
} else {
|
||||
|
|
@ -661,7 +670,9 @@ final class GiftOptionsScreenComponent: Component {
|
|||
case let .generic(gift):
|
||||
if gift.flags.contains(.isAuction) {
|
||||
var action = environment.strings.Gift_Options_Gift_JoinAuction
|
||||
if gift.availability?.remains == 0 {
|
||||
if let auctionStartDate = gift.auctionStartDate, currentTime < auctionStartDate {
|
||||
action = environment.strings.Gift_Options_Gift_ViewAuction
|
||||
} else if gift.availability?.remains == 0 {
|
||||
action = environment.strings.Gift_Options_Gift_ViewAuction
|
||||
}
|
||||
subject = .starGift(gift: gift, price: action)
|
||||
|
|
|
|||
|
|
@ -46,11 +46,11 @@ swift_library(
|
|||
"//submodules/InAppPurchaseManager",
|
||||
"//submodules/Components/BlurredBackgroundComponent",
|
||||
"//submodules/ProgressNavigationButtonNode",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
||||
"//submodules/ConfettiEffect",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/MessageInputPanelComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
|
|||
case let .starGift(gift):
|
||||
media = [
|
||||
TelegramMediaAction(
|
||||
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: gift.upgradeStars != nil, upgradeStars: item.upgradeStars, isRefunded: false, isPrepaidUpgrade: false, upgradeMessageId: nil, peerId: nil, senderId: nil, savedId: nil, prepaidUpgradeHash: nil, giftMessageId: nil, upgradeSeparate: false, isAuctionAcquired: false, toPeerId: nil)
|
||||
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: gift.upgradeStars != nil, upgradeStars: item.upgradeStars, isRefunded: false, isPrepaidUpgrade: false, upgradeMessageId: nil, peerId: nil, senderId: nil, savedId: nil, prepaidUpgradeHash: nil, giftMessageId: nil, upgradeSeparate: false, isAuctionAcquired: false, toPeerId: nil, number: nil)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import InAppPurchaseManager
|
|||
import BlurredBackgroundComponent
|
||||
import ProgressNavigationButtonNode
|
||||
import Markdown
|
||||
import GiftViewScreen
|
||||
import UndoUI
|
||||
import ConfettiEffect
|
||||
import EdgeEffect
|
||||
|
|
@ -1596,10 +1595,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||
guard let self, let component = self.component, let controller = self.environment?.controller(), let upgradePreview else {
|
||||
return
|
||||
}
|
||||
let previewController = GiftViewScreen(
|
||||
context: component.context,
|
||||
subject: .upgradePreview(upgradePreview.attributes, peerName)
|
||||
)
|
||||
let previewController = component.context.sharedContext.makeGiftUpgradePreviewScreen(context: component.context, attributes: upgradePreview.attributes, peerName: peerName)
|
||||
controller.push(previewController)
|
||||
})
|
||||
}
|
||||
|
|
@ -1712,7 +1708,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||
var remains: Int32 = availability.remains
|
||||
if let auctionState = self.giftAuctionState {
|
||||
switch auctionState.auctionState {
|
||||
case let .ongoing(_, _, _, _, _, _, _, giftsLeft, _, _):
|
||||
case let .ongoing(_, _, _, _, _, _, _, giftsLeft, _, _, _, _):
|
||||
remains = giftsLeft
|
||||
case .finished:
|
||||
remains = 0
|
||||
|
|
@ -1839,7 +1835,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||
)))
|
||||
if let giftAuctionState = self.giftAuctionState {
|
||||
switch giftAuctionState.auctionState {
|
||||
case let .ongoing(_, _, endTime, _, _, _, _, _, _, _):
|
||||
case let .ongoing(_, _, endTime, _, _, _, _, _, _, _, _, _):
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
let endTimeout = max(0, endTime - currentTime)
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/BotPaymentsUI",
|
||||
"//submodules/TelegramUI/Components/SegmentControlComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -328,11 +328,21 @@ private final class GiftAuctionAcquiredScreenComponent: Component {
|
|||
|
||||
|
||||
var giftSubject: GiftItemComponent.Subject?
|
||||
var giftTitle: String = ""
|
||||
if case let .generic(gift) = component.gift {
|
||||
giftSubject = .starGift(gift: gift, price: "")
|
||||
giftTitle = gift.title ?? ""
|
||||
}
|
||||
|
||||
if let giftSubject {
|
||||
let titleString: String
|
||||
if let number = gift.number {
|
||||
let fullGiftTitle = "\(giftTitle) #\(formatCollectibleNumber(number, dateTimeFormat: environment.dateTimeFormat))"
|
||||
titleString = environment.strings.Gift_Acquired_GiftRound(fullGiftTitle, "\(gift.round)").string
|
||||
} else {
|
||||
titleString = environment.strings.Gift_Acquired_Round("\(gift.round)").string
|
||||
}
|
||||
|
||||
items.append(.init(
|
||||
id: "header",
|
||||
title: nil,
|
||||
|
|
@ -351,7 +361,7 @@ private final class GiftAuctionAcquiredScreenComponent: Component {
|
|||
AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Acquired_Round("\(gift.round)").string, font: tableBoldFont, textColor: tableTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: titleString, font: tableBoldFont, textColor: tableTextColor)))
|
||||
)
|
||||
)
|
||||
], spacing: 1.0))
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@ private final class ActiveAuctionComponent: Component {
|
|||
var titleText: String = ""
|
||||
var subtitleText: String = ""
|
||||
var subtitleTextColor = component.theme.list.itemPrimaryTextColor
|
||||
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, currentRound, totalRound) = component.state.auctionState, let myBid = component.state.myState.bidAmount {
|
||||
if case let .ongoing(_, _, _, _, _, _, nextRoundDate, _, currentRound, totalRound, _, _) = component.state.auctionState, let myBid = component.state.myState.bidAmount {
|
||||
titleText = component.strings.Gift_ActiveAuctions_Round("\(currentRound)", "\(totalRound)").string
|
||||
|
||||
let bidString = "#\(presentationStringsFormattedNumber(Int32(clamping: myBid), component.dateTimeFormat.groupingSeparator))"
|
||||
|
|
|
|||
|
|
@ -216,6 +216,12 @@ private final class BadgeComponent: Component {
|
|||
subtitleView.alpha = 1.0
|
||||
}
|
||||
badgeOffset.y -= 6.0 + UIScreenPixel
|
||||
|
||||
let subtitleBadgeWidth = subtitleSize.width + 60.0
|
||||
if subtitleBadgeWidth > badgeWidth {
|
||||
badgeOffset.x -= (subtitleBadgeWidth - badgeWidth) * 0.5
|
||||
badgeWidth = subtitleBadgeWidth
|
||||
}
|
||||
} else if let subtitleView = self.subtitle.view {
|
||||
subtitleView.alpha = 0.0
|
||||
}
|
||||
|
|
@ -450,7 +456,7 @@ private final class PeerPlaceComponent: Component {
|
|||
if let place = component.place {
|
||||
placeString = presentationStringsFormattedNumber(place, component.groupingSeparator)
|
||||
if place >= 100 {
|
||||
placeString = "\(placeString)+"
|
||||
placeString = "\(compactNumericCountString(Int(place), decimalSeparator: ".", showDecimalPart: false))+"
|
||||
}
|
||||
} else {
|
||||
placeString = "–"
|
||||
|
|
@ -1141,8 +1147,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
private let sliderPlus = ComponentView<Empty>()
|
||||
private let badge = ComponentView<Empty>()
|
||||
|
||||
private var liveStreamPerks: [ComponentView<Empty>] = []
|
||||
private var liveStreamMessagePreview: ComponentView<Empty>?
|
||||
private var auctionStats: [ComponentView<Empty>] = []
|
||||
|
||||
private let myGifts = ComponentView<Empty>()
|
||||
|
||||
|
|
@ -1818,7 +1823,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
}
|
||||
|
||||
var minBidAmount: Int64 = 100
|
||||
if case let .ongoing(_, _, _, auctionMinBidAmount, _, _, _, _, _, _) = auctionState.auctionState {
|
||||
if case let .ongoing(_, _, _, auctionMinBidAmount, _, _, _, _, _, _, _, _) = auctionState.auctionState {
|
||||
minBidAmount = auctionMinBidAmount
|
||||
if let myMinBidAmount = auctionState.myState.minBidAmount {
|
||||
minBidAmount = myMinBidAmount
|
||||
|
|
@ -1859,7 +1864,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
}
|
||||
var minBidAmount: Int64 = 100
|
||||
var maxBidAmount: Int64 = 50000
|
||||
if case let .ongoing(_, _, _, auctionMinBidAmount, bidLevels, _, _, _, _, _) = state.auctionState {
|
||||
if case let .ongoing(_, _, _, auctionMinBidAmount, bidLevels, _, _, _, _, _, _, _) = state.auctionState {
|
||||
minBidAmount = auctionMinBidAmount
|
||||
if let firstLevel = bidLevels.first(where: { $0.position == 1 }) {
|
||||
maxBidAmount = max(maxBidAmount, Int64(Double(firstLevel.amount) * 1.5))
|
||||
|
|
@ -2193,7 +2198,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
var topBidsComponents: [(EnginePeer.Id, AnyComponent<Empty>)] = []
|
||||
|
||||
let place: Int32
|
||||
if let giftAuctionState = self.giftAuctionState, case let .ongoing(_, _, _, _, bidLevels, topBidders, _, _, _, _) = giftAuctionState.auctionState {
|
||||
if let giftAuctionState = self.giftAuctionState, case let .ongoing(_, _, _, _, bidLevels, topBidders, _, _, _, _, _, lastGiftNumber) = giftAuctionState.auctionState {
|
||||
var myBidAmount = Int64(self.amount.realValue)
|
||||
var myBidDate = currentTime
|
||||
var isBiddingUp = true
|
||||
|
|
@ -2209,6 +2214,9 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
var bidTitle: String
|
||||
var bidTitleColor: UIColor
|
||||
var bidStatus: PeerComponent.Status?
|
||||
|
||||
var giftTitle: String?
|
||||
var giftNumber: Int32?
|
||||
if isBiddingUp {
|
||||
bidTitleColor = environment.theme.list.itemSecondaryTextColor
|
||||
bidTitle = environment.strings.Gift_AuctionBid_BidPreview
|
||||
|
|
@ -2222,12 +2230,16 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
bidStatus = .outbid
|
||||
} else {
|
||||
bidTitle = environment.strings.Gift_AuctionBid_Winning
|
||||
bidTitleColor = environment.theme.list.itemDisclosureActions.constructive.fillColor
|
||||
bidTitleColor = UIColor(rgb: 0x53a939)
|
||||
bidStatus = .winning
|
||||
if case let .generic(gift) = giftAuctionState.gift {
|
||||
giftTitle = gift.title
|
||||
giftNumber = lastGiftNumber + place
|
||||
}
|
||||
}
|
||||
|
||||
if let peer = self.peersMap[component.context.account.peerId] {
|
||||
myBidTitleComponent = AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: bidTitle.uppercased(), font: Font.medium(13.0), textColor: bidTitleColor))))
|
||||
myBidTitleComponent = AnyComponent(PeerHeaderComponent(color: bidTitleColor, dateTimeFormat: environment.dateTimeFormat, title: bidTitle, giftTitle: giftTitle, giftNumber: giftNumber))
|
||||
myBidComponent = AnyComponent(PeerComponent(context: component.context, theme: environment.theme, groupingSeparator: environment.dateTimeFormat.groupingSeparator, peer: peer, place: place, amount: myBidAmount, status: bidStatus, isLast: true, action: nil))
|
||||
}
|
||||
|
||||
|
|
@ -2245,7 +2257,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
}
|
||||
|
||||
if !topBidsComponents.isEmpty {
|
||||
topBidsTitleComponent = AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_AuctionBid_TopWinners.uppercased(), font: Font.medium(13.0), textColor: environment.theme.list.itemSecondaryTextColor))))
|
||||
topBidsTitleComponent = AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_AuctionBid_TopWinnersTotal("\(giftsPerRound)").string.uppercased(), font: Font.medium(13.0), textColor: environment.theme.list.itemSecondaryTextColor))))
|
||||
}
|
||||
} else {
|
||||
place = 1
|
||||
|
|
@ -2253,7 +2265,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
|
||||
var topCutoffRealValue: Int?
|
||||
if place > giftsPerRound {
|
||||
if let giftAuctionState = self.giftAuctionState, case let .ongoing(_, _, _, _, bidLevels, _, _, _, _, _) = giftAuctionState.auctionState {
|
||||
if let giftAuctionState = self.giftAuctionState, case let .ongoing(_, _, _, _, bidLevels, _, _, _, _, _, _, _) = giftAuctionState.auctionState {
|
||||
for bidLevel in bidLevels {
|
||||
if bidLevel.position == giftsPerRound - 1 {
|
||||
topCutoffRealValue = Int(bidLevel.amount)
|
||||
|
|
@ -2366,154 +2378,173 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
self.badgeStars.update(size: starsRect.size, color: sliderColor, emitterPosition: CGPoint(x: badgeFrame.midX, y: badgeFrame.maxY - 32.0))
|
||||
}
|
||||
|
||||
var perks: [([AnimatedTextComponent.Item], String)] = []
|
||||
var auctionStats: [([AnimatedTextComponent.Item], String)] = []
|
||||
|
||||
var minBidAnimatedItems: [AnimatedTextComponent.Item] = []
|
||||
var untilNextDropAnimatedItems: [AnimatedTextComponent.Item] = []
|
||||
var untilNextRoundAnimatedItems: [AnimatedTextComponent.Item] = []
|
||||
var dropsLeftAnimatedItems: [AnimatedTextComponent.Item] = []
|
||||
|
||||
if let auctionState = self.giftAuctionState?.auctionState {
|
||||
if case let .ongoing(_, _, _, minBidAmount, _, _, nextDropDate, dropsLeft, _, _) = auctionState {
|
||||
var minBidAmount = minBidAmount
|
||||
if let myMinBidAmmount = self.giftAuctionState?.myState.minBidAmount {
|
||||
minBidAmount = myMinBidAmmount
|
||||
}
|
||||
var minBidString: String
|
||||
if minBidAmount > 99999 {
|
||||
minBidString = compactNumericCountString(Int(minBidAmount), decimalSeparator: environment.dateTimeFormat.decimalSeparator, showDecimalPart: false)
|
||||
} else {
|
||||
minBidString = presentationStringsFormattedNumber(Int32(clamping: minBidAmount), environment.dateTimeFormat.groupingSeparator)
|
||||
}
|
||||
minBidString = "# \(minBidString)"
|
||||
if let hashIndex = minBidString.firstIndex(of: "#") {
|
||||
var prefix = String(minBidString[..<hashIndex])
|
||||
if !prefix.isEmpty {
|
||||
prefix.removeLast()
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .text(prefix)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var nextRoundTitle = environment.strings.Gift_AuctionBid_UntilNext
|
||||
if let auctionState = self.giftAuctionState?.auctionState, case let .ongoing(_, startDate, _, minBidAmount, _, _, nextDropDate, dropsLeft, currentRound, totalRounds, _, _) = auctionState {
|
||||
if currentTime < startDate {
|
||||
nextRoundTitle = environment.strings.Gift_AuctionBid_BeforeStart
|
||||
} else if currentRound == totalRounds {
|
||||
nextRoundTitle = environment.strings.Gift_AuctionBid_UntilEnd
|
||||
}
|
||||
|
||||
var minBidAmount = minBidAmount
|
||||
if let myMinBidAmmount = self.giftAuctionState?.myState.minBidAmount {
|
||||
minBidAmount = myMinBidAmmount
|
||||
}
|
||||
var minBidString: String
|
||||
if minBidAmount > 99999 {
|
||||
minBidString = compactNumericCountString(Int(minBidAmount), decimalSeparator: environment.dateTimeFormat.decimalSeparator, showDecimalPart: false)
|
||||
} else {
|
||||
minBidString = presentationStringsFormattedNumber(Int32(clamping: minBidAmount), environment.dateTimeFormat.groupingSeparator)
|
||||
}
|
||||
minBidString = "# \(minBidString)"
|
||||
if let hashIndex = minBidString.firstIndex(of: "#") {
|
||||
var prefix = String(minBidString[..<hashIndex])
|
||||
if !prefix.isEmpty {
|
||||
prefix.removeLast()
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .icon("Premium/Stars/StarMedium", tint: false, offset: CGPoint(x: 1.0, y: 2.0 - UIScreenPixel))
|
||||
content: .text(prefix)
|
||||
)
|
||||
)
|
||||
|
||||
let suffixStart = minBidString.index(after: hashIndex)
|
||||
let suffix = minBidString[suffixStart...]
|
||||
|
||||
var i = suffix.startIndex
|
||||
while i < suffix.endIndex {
|
||||
if suffix[i].isNumber {
|
||||
var j = i
|
||||
while j < suffix.endIndex, suffix[j].isNumber {
|
||||
j = suffix.index(after: j)
|
||||
}
|
||||
let string = suffix[i..<j]
|
||||
if let value = Int(string) {
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .number(value, minDigits: string.count)
|
||||
)
|
||||
)
|
||||
}
|
||||
i = j
|
||||
} else {
|
||||
var j = i
|
||||
while j < suffix.endIndex, !suffix[j].isNumber {
|
||||
j = suffix.index(after: j)
|
||||
}
|
||||
let textRun = String(suffix[i..<j])
|
||||
if !textRun.isEmpty {
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .text(textRun)
|
||||
)
|
||||
)
|
||||
}
|
||||
i = j
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minBidAnimatedItems.append(AnimatedTextComponent.Item(id: "static", content: .text(minBidString)))
|
||||
}
|
||||
|
||||
let dropTimeout = max(0, nextDropDate - currentTime)
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .icon("Premium/Stars/StarMedium", tint: false, offset: CGPoint(x: 1.0, y: 2.0 - UIScreenPixel))
|
||||
)
|
||||
)
|
||||
|
||||
let minutes = Int(dropTimeout / 60)
|
||||
let seconds = Int(dropTimeout % 60)
|
||||
let suffixStart = minBidString.index(after: hashIndex)
|
||||
let suffix = minBidString[suffixStart...]
|
||||
|
||||
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
||||
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "colon", content: .text(":")))
|
||||
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
||||
|
||||
dropsLeftAnimatedItems = [AnimatedTextComponent.Item(id: "drops", content: .number(Int(dropsLeft), minDigits: 1))]
|
||||
var i = suffix.startIndex
|
||||
while i < suffix.endIndex {
|
||||
if suffix[i].isNumber {
|
||||
var j = i
|
||||
while j < suffix.endIndex, suffix[j].isNumber {
|
||||
j = suffix.index(after: j)
|
||||
}
|
||||
let string = suffix[i..<j]
|
||||
if let value = Int(string) {
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .number(value, minDigits: string.count)
|
||||
)
|
||||
)
|
||||
}
|
||||
i = j
|
||||
} else {
|
||||
var j = i
|
||||
while j < suffix.endIndex, !suffix[j].isNumber {
|
||||
j = suffix.index(after: j)
|
||||
}
|
||||
let textRun = String(suffix[i..<j])
|
||||
if !textRun.isEmpty {
|
||||
minBidAnimatedItems.append(
|
||||
AnimatedTextComponent.Item(
|
||||
id: AnyHashable(minBidAnimatedItems.count),
|
||||
content: .text(textRun)
|
||||
)
|
||||
)
|
||||
}
|
||||
i = j
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minBidAnimatedItems.append(AnimatedTextComponent.Item(id: "static", content: .text(minBidString)))
|
||||
}
|
||||
|
||||
let dropTimeout: Int32
|
||||
if currentTime < startDate {
|
||||
dropTimeout = max(0, startDate - currentTime)
|
||||
} else {
|
||||
dropTimeout = max(0, nextDropDate - currentTime)
|
||||
}
|
||||
|
||||
let hours = Int(dropTimeout / 3600)
|
||||
let minutes = Int((dropTimeout % 3600) / 60)
|
||||
let seconds = Int(dropTimeout % 60)
|
||||
|
||||
if hours > 0 {
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "h", content: .number(hours, minDigits: 1)))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "colon1", content: .text(":")))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "colon2", content: .text(":")))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
||||
} else {
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "colon", content: .text(":")))
|
||||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
||||
}
|
||||
|
||||
dropsLeftAnimatedItems = [AnimatedTextComponent.Item(id: "drops", content: .number(Int(dropsLeft), minDigits: 1))]
|
||||
}
|
||||
|
||||
perks.append((
|
||||
auctionStats.append((
|
||||
minBidAnimatedItems,
|
||||
environment.strings.Gift_AuctionBid_MinimumBid
|
||||
))
|
||||
|
||||
perks.append((
|
||||
untilNextDropAnimatedItems,
|
||||
environment.strings.Gift_AuctionBid_UntilNext
|
||||
auctionStats.append((
|
||||
untilNextRoundAnimatedItems,
|
||||
nextRoundTitle
|
||||
))
|
||||
|
||||
perks.append((
|
||||
auctionStats.append((
|
||||
dropsLeftAnimatedItems,
|
||||
environment.strings.Gift_AuctionBid_Left
|
||||
))
|
||||
|
||||
contentHeight += 54.0
|
||||
|
||||
let perkHeight: CGFloat = 60.0
|
||||
let perkSpacing: CGFloat = 10.0
|
||||
let perkWidth: CGFloat = floor((availableSize.width - sideInset * 2.0 - perkSpacing * CGFloat(perks.count - 1)) / CGFloat(perks.count))
|
||||
let statSpacing: CGFloat = 10.0
|
||||
let statWidth: CGFloat = floor((availableSize.width - sideInset * 2.0 - statSpacing * CGFloat(auctionStats.count - 1)) / CGFloat(auctionStats.count))
|
||||
let statHeight: CGFloat = 60.0
|
||||
|
||||
for i in 0 ..< perks.count {
|
||||
var perkFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i) * (perkWidth + perkSpacing), y: contentHeight), size: CGSize(width: perkWidth, height: perkHeight))
|
||||
if i == perks.count - 1 {
|
||||
perkFrame.size.width = max(0.0, availableSize.width - sideInset - perkFrame.minX)
|
||||
for i in 0 ..< auctionStats.count {
|
||||
var statFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i) * (statWidth + statSpacing), y: contentHeight), size: CGSize(width: statWidth, height: statHeight))
|
||||
if i == auctionStats.count - 1 {
|
||||
statFrame.size.width = max(0.0, availableSize.width - sideInset - statFrame.minX)
|
||||
}
|
||||
let perkView: ComponentView<Empty>
|
||||
if self.liveStreamPerks.count > i {
|
||||
perkView = self.liveStreamPerks[i]
|
||||
let statView: ComponentView<Empty>
|
||||
if self.auctionStats.count > i {
|
||||
statView = self.auctionStats[i]
|
||||
} else {
|
||||
perkView = ComponentView()
|
||||
self.liveStreamPerks.append(perkView)
|
||||
statView = ComponentView()
|
||||
self.auctionStats.append(statView)
|
||||
}
|
||||
let perk = perks[i]
|
||||
let _ = perkView.update(
|
||||
let stat = auctionStats[i]
|
||||
let _ = statView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(AuctionStatComponent(
|
||||
context: component.context,
|
||||
gift: i == perks.count - 1 ? component.auctionContext.gift : nil,
|
||||
title: perk.0,
|
||||
subtitle: perk.1,
|
||||
gift: i == auctionStats.count - 1 ? component.auctionContext.gift : nil,
|
||||
title: stat.0,
|
||||
subtitle: stat.1,
|
||||
small: false,
|
||||
theme: environment.theme
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: perkFrame.size
|
||||
containerSize: statFrame.size
|
||||
)
|
||||
if let perkComponentView = perkView.view {
|
||||
if let perkComponentView = statView.view {
|
||||
if perkComponentView.superview == nil {
|
||||
self.scrollContentView.addSubview(perkComponentView)
|
||||
}
|
||||
transition.setFrame(view: perkComponentView, frame: perkFrame)
|
||||
transition.setFrame(view: perkComponentView, frame: statFrame)
|
||||
}
|
||||
}
|
||||
|
||||
contentHeight += perkHeight
|
||||
contentHeight += statHeight
|
||||
contentHeight += 24.0
|
||||
|
||||
let acquiredGiftsCount = self.giftAuctionState?.myState.acquiredCount ?? 0
|
||||
|
|
@ -2666,10 +2697,18 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
|
||||
var subtitleString = environment.strings.Gift_AuctionBid_Subtitle("\(giftsPerRound)").string
|
||||
if let auctionState = self.giftAuctionState?.auctionState, case let .ongoing(_, _, _, _, _, _, _, _, currentRound, totalRounds, _, _) = auctionState {
|
||||
subtitleString = environment.strings.Gift_AuctionBid_RoundSubtitle(
|
||||
presentationStringsFormattedNumber(currentRound, environment.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(totalRounds, environment.dateTimeFormat.groupingSeparator),
|
||||
).string
|
||||
}
|
||||
|
||||
let subtitleSize = self.subtitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.Gift_AuctionBid_Subtitle("\(giftsPerRound)").string, font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor))
|
||||
text: .plain(NSAttributedString(string: subtitleString, font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -3475,3 +3514,101 @@ private final class GiftViewContextReferenceContentSource: ContextReferenceConte
|
|||
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerHeaderComponent: CombinedComponent {
|
||||
let color: UIColor
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let title: String
|
||||
let giftTitle: String?
|
||||
let giftNumber: Int32?
|
||||
|
||||
public init(
|
||||
color: UIColor,
|
||||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
title: String,
|
||||
giftTitle: String?,
|
||||
giftNumber: Int32?
|
||||
) {
|
||||
self.color = color
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.title = title
|
||||
self.giftTitle = giftTitle
|
||||
self.giftNumber = giftNumber
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerHeaderComponent, rhs: PeerHeaderComponent) -> Bool {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.giftTitle != rhs.giftTitle {
|
||||
return false
|
||||
}
|
||||
if lhs.giftNumber != rhs.giftNumber {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
|
||||
let background = Child(RoundedRectangle.self)
|
||||
let giftTitle = Child(MultilineTextComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.title.uppercased(), font: Font.medium(13.0), textColor: component.color))
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: title.size.width / 2.0, y: title.size.height / 2.0))
|
||||
)
|
||||
|
||||
var contentSize = title.size
|
||||
if let titleString = component.giftTitle, let giftNumber = component.giftNumber {
|
||||
let giftTitle = giftTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "\(titleString) #\(formatCollectibleNumber(giftNumber, dateTimeFormat: component.dateTimeFormat))", font: Font.regular(12.0), textColor: component.color))
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 6.0
|
||||
let padding: CGFloat = 5.0
|
||||
let backgroundSize = CGSize(width: giftTitle.size.width + padding * 2.0, height: giftTitle.size.height + 4.0)
|
||||
let background = background.update(
|
||||
component: RoundedRectangle(
|
||||
color: component.color.withAlphaComponent(0.1),
|
||||
cornerRadius: backgroundSize.height / 2.0
|
||||
),
|
||||
availableSize: backgroundSize,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(background
|
||||
.position(CGPoint(x: title.size.width + spacing + padding + giftTitle.size.width / 2.0, y: title.size.height / 2.0))
|
||||
.appear(.default())
|
||||
.disappear(.default())
|
||||
)
|
||||
|
||||
context.add(giftTitle
|
||||
.position(CGPoint(x: title.size.width + spacing + padding + giftTitle.size.width / 2.0, y: title.size.height / 2.0))
|
||||
.appear(.default())
|
||||
.disappear(.default())
|
||||
)
|
||||
|
||||
contentSize.width += spacing + backgroundSize.width
|
||||
}
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ private final class GiftAuctionInfoSheetContent: CombinedComponent {
|
|||
|
||||
let _ = (self.auctionContext.state
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] state in
|
||||
if let self, case let .ongoing(_, _, _, _, _, _, _, _, _, totalRounds) = state?.auctionState {
|
||||
if let self, case let .ongoing(_, _, _, _, _, _, _, _, _, totalRounds, _, _) = state?.auctionState {
|
||||
self.rounds = totalRounds
|
||||
self.updated()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,427 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ComponentFlow
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import MultilineTextComponent
|
||||
import MultilineTextWithEntitiesComponent
|
||||
import BundleIconComponent
|
||||
import ButtonComponent
|
||||
import Markdown
|
||||
import BalancedTextComponent
|
||||
import AvatarNode
|
||||
import TextFormat
|
||||
import TelegramStringFormatting
|
||||
import StarsAvatarComponent
|
||||
import EmojiTextAttachmentView
|
||||
import EmojiStatusComponent
|
||||
import UndoUI
|
||||
import ConfettiEffect
|
||||
import PlainButtonComponent
|
||||
import CheckComponent
|
||||
import TooltipUI
|
||||
import LottieComponent
|
||||
import ContextUI
|
||||
import TelegramNotices
|
||||
import PremiumLockButtonSubtitleComponent
|
||||
import StarsBalanceOverlayComponent
|
||||
import BalanceNeededScreen
|
||||
import GiftItemComponent
|
||||
import GiftAnimationComponent
|
||||
import ChatThemeScreen
|
||||
import ProfileLevelRatingBarComponent
|
||||
import AnimatedTextComponent
|
||||
|
||||
private final class GiftAuctionUpgradePreviewSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let gift: StarGift
|
||||
let animateOut: ActionSlot<Action<()>>
|
||||
let getController: () -> ViewController?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
gift: StarGift,
|
||||
animateOut: ActionSlot<Action<()>>,
|
||||
getController: @escaping () -> ViewController?
|
||||
) {
|
||||
self.context = context
|
||||
self.gift = gift
|
||||
self.animateOut = animateOut
|
||||
self.getController = getController
|
||||
}
|
||||
|
||||
static func ==(lhs: GiftAuctionUpgradePreviewSheetContent, rhs: GiftAuctionUpgradePreviewSheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.gift != rhs.gift {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
|
||||
private var peerDisposable: Disposable?
|
||||
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
var cachedSmallChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
var upgradePreviewTimer: SwiftSignalKit.Timer?
|
||||
|
||||
init(
|
||||
context: AccountContext
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
super.init()
|
||||
|
||||
let peerIds: [EnginePeer.Id] = [context.account.peerId]
|
||||
|
||||
self.peerDisposable = (
|
||||
context.engine.data.get(EngineDataMap(
|
||||
peerIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.Peer in
|
||||
return TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
}
|
||||
))
|
||||
|> deliverOnMainQueue
|
||||
).startStrict(next: { [weak self] peers in
|
||||
if let strongSelf = self {
|
||||
var peersMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||
for (peerId, maybePeer) in peers {
|
||||
if let peer = maybePeer {
|
||||
peersMap[peerId] = peer
|
||||
}
|
||||
}
|
||||
strongSelf.peerMap = peersMap
|
||||
strongSelf.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
|
||||
self.upgradePreviewTimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in
|
||||
self?.upgradePreviewTimerTick()
|
||||
}, queue: Queue.mainQueue())
|
||||
self.upgradePreviewTimer?.start()
|
||||
self.upgradePreviewTimerTick()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.peerDisposable?.dispose()
|
||||
self.upgradePreviewTimer?.invalidate()
|
||||
}
|
||||
|
||||
private func upgradePreviewTimerTick() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func makeState() -> State {
|
||||
return State(context: self.context)
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let animation = Child(GiftCompositionComponent.self)
|
||||
let avatar = Child(AvatarComponent.self)
|
||||
let peerName = Child(MultilineTextComponent.self)
|
||||
//let description = Child(MultilineTextComponent.self)
|
||||
|
||||
//let initialGift = Child(GiftItemComponent.self)
|
||||
//let upgradedGift = Child(GiftItemComponent.self)
|
||||
|
||||
let button = Child(ButtonComponent.self)
|
||||
|
||||
let giftCompositionExternalState = GiftCompositionComponent.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let component = context.component
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder
|
||||
let controller = environment.controller
|
||||
let state = context.state
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let headerHeight: CGFloat = 226.0
|
||||
let headerSubject: GiftCompositionComponent.Subject = .preview([])
|
||||
|
||||
var peerNameString = ""
|
||||
if let peer = state.peerMap[component.context.account.peerId] {
|
||||
peerNameString = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
|
||||
let peerName = peerName.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: peerNameString,
|
||||
font: Font.bold(20.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let animationOffset: CGPoint? = CGPoint(x: peerName.size.width / 2.0 + 20.0 - 12.0, y: 79.0)
|
||||
let animationScale: CGFloat = 0.19
|
||||
|
||||
let animation = animation.update(
|
||||
component: GiftCompositionComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
subject: headerSubject,
|
||||
animationOffset: animationOffset,
|
||||
animationScale: animationScale,
|
||||
displayAnimationStars: true,
|
||||
externalState: giftCompositionExternalState,
|
||||
requestUpdate: { [weak state] transition in
|
||||
state?.updated(transition: transition)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: headerHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(animation
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight / 2.0))
|
||||
)
|
||||
contentHeight += headerHeight
|
||||
|
||||
if let peer = state.peerMap[component.context.account.peerId] {
|
||||
let avatar = avatar.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
peer: peer
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(avatar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 86.0))
|
||||
)
|
||||
}
|
||||
context.add(peerName
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 167.0))
|
||||
)
|
||||
|
||||
// let vibrantColor: UIColor
|
||||
// if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
|
||||
// vibrantColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
// } else {
|
||||
// vibrantColor = UIColor.white.withAlphaComponent(0.6)
|
||||
// }
|
||||
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let buttonSize = CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
let buttonBackground = ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
)
|
||||
let button = button.update(
|
||||
component: ButtonComponent(
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("ok"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Common_OK, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: {
|
||||
if let controller = controller() as? GiftAuctionUpgradePreviewScreen {
|
||||
controller.dismissAnimated()
|
||||
}
|
||||
}),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: contentHeight), size: button.size)
|
||||
context.add(button
|
||||
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
||||
)
|
||||
contentHeight += button.size.height
|
||||
contentHeight += 7.0
|
||||
|
||||
let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom
|
||||
return CGSize(width: context.availableSize.width, height: contentHeight + 5.0 + effectiveBottomInset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class GiftAuctionUpgradePreviewSheetComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let gift: StarGift
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
gift: StarGift
|
||||
) {
|
||||
self.context = context
|
||||
self.gift = gift
|
||||
}
|
||||
|
||||
static func ==(lhs: GiftAuctionUpgradePreviewSheetComponent, rhs: GiftAuctionUpgradePreviewSheetComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.gift != rhs.gift {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(GiftAuctionUpgradePreviewSheetContent(
|
||||
context: context.component.context,
|
||||
gift: context.component.gift,
|
||||
animateOut: animateOut,
|
||||
getController: controller
|
||||
)),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
clipsContent: true,
|
||||
hasDimView: false,
|
||||
autoAnimateOut: false,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut,
|
||||
onPan: {
|
||||
},
|
||||
willDismiss: {
|
||||
if let controller = controller() as? GiftAuctionUpgradePreviewScreen {
|
||||
controller.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.3).withUserData(ViewControllerComponentContainer.AnimateOutTransition()))
|
||||
}
|
||||
}
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
SheetComponentEnvironment(
|
||||
isDisplaying: environment.value.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
hasInputHeight: !environment.inputHeight.isZero,
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
if let controller = controller() as? GiftAuctionUpgradePreviewScreen {
|
||||
controller.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.3).withUserData(ViewControllerComponentContainer.AnimateOutTransition()))
|
||||
animateOut.invoke(Action { _ in
|
||||
controller.dismiss(completion: nil)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let controller = controller() as? GiftAuctionUpgradePreviewScreen {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(sheet
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
var sideInset: CGFloat = 0.0
|
||||
var bottomInset: CGFloat = max(environment.safeInsets.bottom, sheetExternalState.contentHeight)
|
||||
if case .regular = environment.metrics.widthClass {
|
||||
sideInset = floor((context.availableSize.width - 430.0) / 2.0) - 12.0
|
||||
bottomInset = (context.availableSize.height - sheetExternalState.contentHeight) / 2.0 + sheetExternalState.contentHeight
|
||||
}
|
||||
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: max(sideInset, environment.safeInsets.left), bottom: 0.0, right: max(sideInset, environment.safeInsets.right)),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GiftAuctionUpgradePreviewScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
gift: StarGift
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
component: GiftAuctionUpgradePreviewSheetComponent(context: context, gift: gift),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,10 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
fileprivate var giftAuctionAcquiredGifts: [GiftAuctionAcquiredGift] = []
|
||||
private var giftAuctionAcquiredGiftsPromise = ValuePromise<[GiftAuctionAcquiredGift]>()
|
||||
private var giftAuctionAcquiredGiftsDisposable = MetaDisposable()
|
||||
|
||||
|
||||
private(set) var giftValueInfo: StarGift.UniqueGift.ValueInfo?
|
||||
private var giftValueInfoDisposable: Disposable?
|
||||
|
||||
var cachedStarImage: (UIImage, PresentationTheme)?
|
||||
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
|
@ -102,6 +105,17 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
if let acquiredCount = auctionState?.myState.acquiredCount, acquiredCount > (previousState?.myState.acquiredCount ?? 0) {
|
||||
self.loadAcquiredGifts()
|
||||
}
|
||||
|
||||
if !"".isEmpty, self.giftValueInfoDisposable == nil, let auctionState, case let .generic(gift) = auctionState.gift, case .finished = auctionState.auctionState, let slug = gift.auctionSlug {
|
||||
self.giftValueInfoDisposable = (self.context.engine.payments.getUniqueStarGiftValueInfo(slug: "\(slug)-1")
|
||||
|> deliverOnMainQueue).start(next: { [weak self] valueInfo in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.giftValueInfo = valueInfo
|
||||
self.updated(transition: .easeInOut(duration: 0.25))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||
|
|
@ -114,6 +128,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
self.disposable?.dispose()
|
||||
self.giftAuctionAcquiredGiftsDisposable.dispose()
|
||||
self.giftAuctionTimer?.invalidate()
|
||||
self.giftValueInfoDisposable?.dispose()
|
||||
}
|
||||
|
||||
func loadAcquiredGifts() {
|
||||
|
|
@ -291,6 +306,26 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
controller.present(shareController, in: .window(.root))
|
||||
}
|
||||
|
||||
func openGiftResale() {
|
||||
guard let controller = self.getController() as? GiftAuctionViewScreen, let gift = self.giftAuctionState?.gift, case let .generic(gift) = gift else {
|
||||
return
|
||||
}
|
||||
let storeController = self.context.sharedContext.makeGiftStoreController(
|
||||
context: self.context,
|
||||
peerId: self.context.account.peerId,
|
||||
gift: gift
|
||||
)
|
||||
controller.push(storeController)
|
||||
}
|
||||
|
||||
func openGiftFragmentResale() {
|
||||
guard let controller = self.getController() as? GiftAuctionViewScreen, let navigationController = controller.navigationController as? NavigationController, let url = self.giftValueInfo?.fragmentListedUrl else {
|
||||
return
|
||||
}
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
}
|
||||
|
||||
func morePressed(view: UIView, gesture: ContextGesture?) {
|
||||
guard let controller = self.getController() as? GiftAuctionViewScreen else {
|
||||
return
|
||||
|
|
@ -367,8 +402,8 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
let button = Child(ButtonComponent.self)
|
||||
|
||||
let acquiredButton = Child(PlainButtonComponent.self)
|
||||
// let telegramSaleButton = Child(PlainButtonComponent.self)
|
||||
// let fragmentSaleButton = Child(PlainButtonComponent.self)
|
||||
let telegramSaleButton = Child(PlainButtonComponent.self)
|
||||
let fragmentSaleButton = Child(PlainButtonComponent.self)
|
||||
|
||||
let moreButtonPlayOnce = ActionSlot<Void>()
|
||||
|
||||
|
|
@ -447,17 +482,23 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
let tableTextColor = theme.list.itemPrimaryTextColor
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
var startTime = currentTime
|
||||
var endTime = currentTime
|
||||
|
||||
var isUpcoming = false
|
||||
var isEnded = false
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
if let auctionState = state.giftAuctionState, case let .generic(gift) = component.auctionContext.gift {
|
||||
startTime = auctionState.startDate
|
||||
endTime = auctionState.endDate
|
||||
if case .finished = auctionState.auctionState {
|
||||
isEnded = true
|
||||
} else if auctionState.endDate < currentTime {
|
||||
isEnded = true
|
||||
}
|
||||
if auctionState.startDate > currentTime {
|
||||
isUpcoming = true
|
||||
}
|
||||
|
||||
if isEnded {
|
||||
descriptionText = strings.Gift_Auction_Ended
|
||||
|
|
@ -539,33 +580,93 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
|
||||
tableItems.append(.init(
|
||||
id: "start",
|
||||
title: strings.Gift_Auction_Started,
|
||||
title: isUpcoming ? strings.Gift_Auction_Start : strings.Gift_Auction_Started,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.startDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
tableItems.append(.init(
|
||||
id: "ends",
|
||||
title: strings.Gift_Auction_Ends,
|
||||
title: isUpcoming ? strings.Gift_Auction_End : strings.Gift_Auction_Ends,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: auctionState.endDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
if case let .ongoing(_, _, _, _, _, _, _, giftsLeft, currentRound, totalRounds) = auctionState.auctionState {
|
||||
tableItems.append(.init(
|
||||
id: "round",
|
||||
title: strings.Gift_Auction_CurrentRound,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_Round("\(currentRound)", "\(totalRounds)").string, font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
tableItems.append(.init(
|
||||
id: "availability",
|
||||
title: strings.Gift_Auction_Availability,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_AvailabilityOf(presentationStringsFormattedNumber(giftsLeft, dateTimeFormat.groupingSeparator), presentationStringsFormattedNumber(gift.availability?.total ?? 0, dateTimeFormat.groupingSeparator)).string, font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
|
||||
if case let .ongoing(_, _, _, _, _, _, _, giftsLeft, currentRound, totalRounds, rounds, _) = auctionState.auctionState {
|
||||
if isUpcoming {
|
||||
tableItems.append(.init(
|
||||
id: "quantity",
|
||||
title: strings.Gift_Auction_Quantity,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(gift.availability?.total ?? 0, dateTimeFormat.groupingSeparator), font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
tableItems.append(.init(
|
||||
id: "rounds",
|
||||
title: strings.Gift_Auction_TotalRounds,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(totalRounds, dateTimeFormat.groupingSeparator), font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
|
||||
for i in 0 ..< rounds.count {
|
||||
let round = rounds[i]
|
||||
let start = round.num
|
||||
var end = totalRounds
|
||||
if i < rounds.count - 1 {
|
||||
let nextRound = rounds[i + 1]
|
||||
end = nextRound.num - 1
|
||||
}
|
||||
|
||||
let title: String = start == end ? strings.Gift_Auction_TimeRound("\(start)").string : strings.Gift_Auction_TimeRounds("\(start)-\(end)").string
|
||||
var value: String
|
||||
if round.duration % 3600 == 0 {
|
||||
let hours = round.duration / 3600
|
||||
value = start == end ? strings.Gift_Auction_Hours(hours) : strings.Gift_Auction_HoursEach(hours)
|
||||
} else {
|
||||
let minutes = round.duration / 60
|
||||
value = start == end ? strings.Gift_Auction_Minutes(minutes) : strings.Gift_Auction_MinutesEach(minutes)
|
||||
}
|
||||
|
||||
if case let .extendable(_, _, top, window) = round {
|
||||
var windowString: String
|
||||
if window % 60 == 0 {
|
||||
windowString = strings.Gift_Auction_Minutes(window)
|
||||
} else {
|
||||
windowString = strings.Gift_Auction_Seconds(window)
|
||||
}
|
||||
value += " \(strings.Gift_Auction_Extension(windowString, "\(top)").string)"
|
||||
}
|
||||
|
||||
tableItems.append(.init(
|
||||
id: "round_\(i)",
|
||||
title: title,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: value, font: tableFont, textColor: tableTextColor)),
|
||||
maximumNumberOfLines: 3
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
} else {
|
||||
tableItems.append(.init(
|
||||
id: "round",
|
||||
title: strings.Gift_Auction_CurrentRound,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_Round("\(currentRound)", "\(totalRounds)").string, font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
tableItems.append(.init(
|
||||
id: "availability",
|
||||
title: strings.Gift_Auction_Availability,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Auction_AvailabilityOf(presentationStringsFormattedNumber(giftsLeft, dateTimeFormat.groupingSeparator), presentationStringsFormattedNumber(gift.availability?.total ?? 0, dateTimeFormat.groupingSeparator)).string, font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -692,6 +793,98 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
hasAdditionalButtons = true
|
||||
}
|
||||
|
||||
if let giftValueInfo = state.giftValueInfo, case let .generic(gift) = component.auctionContext.gift {
|
||||
if let listedCount = giftValueInfo.listedCount, listedCount > 0 {
|
||||
originY += 5.0
|
||||
|
||||
let telegramSaleButton = telegramSaleButton.update(
|
||||
component: PlainButtonComponent(content: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "count", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
|
||||
Rectangle(color: .clear, width: 8.0, height: 1.0)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .starGift(gift: gift, price: ""),
|
||||
mode: .buttonIcon
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "text", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnTelegram)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
||||
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
||||
))
|
||||
], spacing: 0.0)
|
||||
), action: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.openGiftResale()
|
||||
}, animateScale: false),
|
||||
availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(telegramSaleButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + telegramSaleButton.size.height / 2.0)))
|
||||
originY += telegramSaleButton.size.height
|
||||
originY += 12.0
|
||||
|
||||
hasAdditionalButtons = true
|
||||
}
|
||||
|
||||
if let listedCount = giftValueInfo.fragmentListedCount, listedCount > 0 {
|
||||
let fragmentSaleButton = fragmentSaleButton.update(
|
||||
component: PlainButtonComponent(content: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "count", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
|
||||
Rectangle(color: .clear, width: 8.0, height: 1.0)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .starGift(gift: gift, price: ""),
|
||||
mode: .buttonIcon
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "text", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnFragment)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
||||
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
||||
))
|
||||
], spacing: 0.0)
|
||||
), action: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.openGiftFragmentResale()
|
||||
}, animateScale: false),
|
||||
availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(fragmentSaleButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + fragmentSaleButton.size.height / 2.0)))
|
||||
originY += fragmentSaleButton.size.height
|
||||
originY += 12.0
|
||||
|
||||
hasAdditionalButtons = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasAdditionalButtons {
|
||||
originY += 21.0
|
||||
}
|
||||
|
|
@ -707,21 +900,32 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
|
||||
let buttonChild: _UpdatedChildComponent
|
||||
if !isEnded {
|
||||
let buttonAttributedString = NSMutableAttributedString(string: strings.Gift_Auction_Join, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
let buttonAttributedString = NSMutableAttributedString(string: isUpcoming ? strings.Gift_Auction_EarlyBid : strings.Gift_Auction_Join, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
|
||||
let endTimeout = max(0, endTime - currentTime)
|
||||
let endTimeout: Int32
|
||||
if currentTime < startTime {
|
||||
endTimeout = max(0, startTime - currentTime)
|
||||
} else {
|
||||
endTimeout = max(0, endTime - currentTime)
|
||||
}
|
||||
|
||||
let hours = Int(endTimeout / 3600)
|
||||
let minutes = Int((endTimeout % 3600) / 60)
|
||||
let seconds = Int(endTimeout % 60)
|
||||
|
||||
let rawString = hours > 0 ? strings.Gift_Auction_TimeLeftHours : strings.Gift_Auction_TimeLeftMinutes
|
||||
let rawString: String
|
||||
if isUpcoming {
|
||||
rawString = hours > 0 ? strings.Gift_Auction_StartsInHours : strings.Gift_Auction_StartsInMinutes
|
||||
} else {
|
||||
rawString = hours > 0 ? strings.Gift_Auction_TimeLeftHours : strings.Gift_Auction_TimeLeftMinutes
|
||||
}
|
||||
|
||||
var buttonAnimatedTitleItems: [AnimatedTextComponent.Item] = []
|
||||
var startIndex = rawString.startIndex
|
||||
while true {
|
||||
if let range = rawString.range(of: "{", range: startIndex ..< rawString.endIndex) {
|
||||
if range.lowerBound != startIndex {
|
||||
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "prefix", content: .text(String(rawString[startIndex ..< range.lowerBound]))))
|
||||
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "prefix_\(buttonAnimatedTitleItems.count)", content: .text(String(rawString[startIndex ..< range.lowerBound]))))
|
||||
}
|
||||
|
||||
startIndex = range.upperBound
|
||||
|
|
@ -742,7 +946,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
if startIndex != rawString.endIndex {
|
||||
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "suffix", content: .text(String(rawString[startIndex ..< rawString.endIndex]))))
|
||||
buttonAnimatedTitleItems.append(AnimatedTextComponent.Item(id: "suffix_\(buttonAnimatedTitleItems.count)", content: .text(String(rawString[startIndex ..< rawString.endIndex]))))
|
||||
}
|
||||
|
||||
let items: [AnyComponentWithIdentity<Empty>] = [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,485 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import AvatarNode
|
||||
import Markdown
|
||||
import GiftItemComponent
|
||||
import ChatMessagePaymentAlertController
|
||||
import ActivityIndicator
|
||||
import TooltipUI
|
||||
import MultilineTextComponent
|
||||
import TelegramStringFormatting
|
||||
|
||||
private final class GiftOfferAlertContentNode: AlertContentNode {
|
||||
private let context: AccountContext
|
||||
private let strings: PresentationStrings
|
||||
private var presentationTheme: PresentationTheme
|
||||
private let title: String
|
||||
private let text: String
|
||||
private let gift: StarGift.UniqueGift
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let giftView = ComponentView<Empty>()
|
||||
private let textNode: ASTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let tableView = ComponentView<Empty>()
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
private let symbolButtonTag = GenericComponentViewTag()
|
||||
|
||||
fileprivate var getController: () -> ViewController? = { return nil}
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var activityIndicator: ActivityIndicator?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
var inProgress = false {
|
||||
didSet {
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: AlertControllerTheme,
|
||||
ptheme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
gift: StarGift.UniqueGift,
|
||||
peer: EnginePeer,
|
||||
title: String,
|
||||
text: String,
|
||||
actions: [TextAlertAction]
|
||||
) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.presentationTheme = ptheme
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.gift = gift
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.displayWithoutProcessing = true
|
||||
|
||||
self.avatarNode = 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.arrowNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
|
||||
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: peer)
|
||||
}
|
||||
|
||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor)
|
||||
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.withAlphaComponent(0.9))
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func dismissAllTooltips() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
controller.window?.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
})
|
||||
controller.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func showAttributeInfo(tag: Any, text: String) {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
guard let sourceView = self.tableView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
controller.present(tooltipController, in: .current)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 310.0)
|
||||
|
||||
let strings = self.strings
|
||||
|
||||
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 giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||
|
||||
let _ = self.giftView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationTheme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: self.gift, price: nil),
|
||||
mode: .thumbnail
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: avatarSize
|
||||
)
|
||||
if let view = self.giftView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = giftFrame
|
||||
}
|
||||
|
||||
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 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)
|
||||
|
||||
origin.y += avatarSize.height + 17.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 + 5.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
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
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)
|
||||
|
||||
let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
|
||||
let tableFont = Font.regular(15.0)
|
||||
let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor
|
||||
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
.model, .pattern, .backdrop, .originalInfo
|
||||
]
|
||||
|
||||
var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:]
|
||||
for attribute in self.gift.attributes {
|
||||
attributeMap[attribute.attributeType] = attribute
|
||||
}
|
||||
|
||||
for type in order {
|
||||
if let attribute = attributeMap[type] {
|
||||
let id: String?
|
||||
let title: String?
|
||||
let value: NSAttributedString
|
||||
let percentage: Float?
|
||||
let tag: AnyObject?
|
||||
|
||||
switch attribute {
|
||||
case let .model(name, _, rarity):
|
||||
id = "model"
|
||||
title = strings.Gift_Unique_Model
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.modelButtonTag
|
||||
case let .backdrop(name, _, _, _, _, _, rarity):
|
||||
id = "backdrop"
|
||||
title = strings.Gift_Unique_Backdrop
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.backdropButtonTag
|
||||
case let .pattern(name, _, rarity):
|
||||
id = "pattern"
|
||||
title = strings.Gift_Unique_Symbol
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.symbolButtonTag
|
||||
case .originalInfo:
|
||||
continue
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(value))
|
||||
)
|
||||
)
|
||||
)
|
||||
if let percentage, let tag {
|
||||
items.append(AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: self.context,
|
||||
text: formatPercentage(percentage),
|
||||
color: self.presentationTheme.list.itemAccentColor
|
||||
)),
|
||||
action: { [weak self] in
|
||||
self?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
}
|
||||
let itemComponent = AnyComponent(
|
||||
HStack(items, spacing: 4.0)
|
||||
)
|
||||
|
||||
tableItems.append(.init(
|
||||
id: id,
|
||||
title: title,
|
||||
hasBackground: false,
|
||||
component: itemComponent
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if let valueAmount = self.gift.valueAmount, let valueCurrency = self.gift.valueCurrency {
|
||||
tableItems.append(.init(
|
||||
id: "fiatValue",
|
||||
title: strings.Gift_Unique_Value,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0)
|
||||
))
|
||||
}
|
||||
|
||||
let tableSize = self.tableView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
TableComponent(
|
||||
theme: self.presentationTheme,
|
||||
items: tableItems,
|
||||
semiTransparent: true
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: contentWidth - 32.0, height: size.height)
|
||||
)
|
||||
let tableFrame = CGRect(origin: CGPoint(x: 16.0, y: avatarSize.height + titleSize.height + textSize.height + 60.0), size: tableSize)
|
||||
if let view = self.tableView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = tableFrame
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + 40.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
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
do {
|
||||
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
|
||||
do {
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
do {
|
||||
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
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let activityIndicator: ActivityIndicator
|
||||
if let current = self.activityIndicator {
|
||||
activityIndicator = current
|
||||
} else {
|
||||
activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false))
|
||||
self.addSubnode(activityIndicator)
|
||||
}
|
||||
|
||||
if let actionNode = self.actionNodes.first {
|
||||
actionNode.isUserInteractionEnabled = false
|
||||
actionNode.isHidden = false
|
||||
|
||||
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
||||
transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||
}
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
|
||||
public func giftOfferAlertController(
|
||||
context: AccountContext,
|
||||
gift: StarGift.UniqueGift,
|
||||
peer: EnginePeer,
|
||||
amount: CurrencyAmount,
|
||||
commit: @escaping () -> Void
|
||||
) -> AlertController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
|
||||
let title = "Confirm Sale"
|
||||
let buttonText: String = "Confirm Sale"
|
||||
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = "\(amount.amount) Stars"
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
||||
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
let finalPriceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
let starsValue = Int32(floor(Float(amount.amount.value) * Float(resaleConfiguration.starGiftCommissionStarsPermille) / 1000.0))
|
||||
finalPriceString = "\(starsValue) Stars"
|
||||
case .ton:
|
||||
let tonValue = Int64(Float(amount.amount.value) * Float(resaleConfiguration.starGiftCommissionTonPermille) / 1000.0)
|
||||
finalPriceString = formatTonAmountText(tonValue, dateTimeFormat: presentationData.dateTimeFormat, maxDecimalPositions: 3) + " TON"
|
||||
}
|
||||
|
||||
let giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
let text = "Do you want to sell **\(giftTitle)** to \(peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) for **\(priceString)**? You'll receive **\(finalPriceString)** after fees."
|
||||
|
||||
|
||||
var contentNode: GiftOfferAlertContentNode?
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { [weak contentNode] in
|
||||
contentNode?.inProgress = true
|
||||
commit()
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?(true)
|
||||
})]
|
||||
|
||||
contentNode = GiftOfferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions)
|
||||
|
||||
let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: nil, chatPeerId: context.account.peerId, showBalance: false)
|
||||
contentNode?.getController = { [weak controller] in
|
||||
return controller
|
||||
}
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
} else {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ private final class GiftTransferAlertContentNode: AlertContentNode {
|
|||
id: "fiatValue",
|
||||
title: strings.Gift_Unique_Value,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "≈\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0)
|
||||
))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import ButtonComponent
|
|||
import PlainButtonComponent
|
||||
import GiftItemComponent
|
||||
import AccountContext
|
||||
import GlassBarButtonComponent
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -62,8 +63,7 @@ private final class SheetContent: CombinedComponent {
|
|||
}
|
||||
|
||||
static var body: Body {
|
||||
let closeButton = Child(Button.self)
|
||||
|
||||
let closeButton = Child(GlassBarButtonComponent.self)
|
||||
let title = Child(BalancedTextComponent.self)
|
||||
let text = Child(BalancedTextComponent.self)
|
||||
let gifts = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
|
|
@ -79,7 +79,6 @@ private final class SheetContent: CombinedComponent {
|
|||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 32.0 + environment.safeInsets.left
|
||||
|
||||
let titleFont = Font.semibold(17.0)
|
||||
|
|
@ -87,20 +86,29 @@ private final class SheetContent: CombinedComponent {
|
|||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 10.0)
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
|
||||
|
||||
let closeButton = closeButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(Text(text: strings.Common_Cancel, font: Font.regular(17.0), color: theme.actionSheet.controlAccentColor)),
|
||||
action: { [weak component] in
|
||||
component?.dismiss()
|
||||
component: GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
component.dismiss()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 100.0, height: 30.0),
|
||||
availableSize: CGSize(width: 40.0, height: 40.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: environment.safeInsets.left + 16.0 + closeButton.size.width / 2.0, y: 28.0))
|
||||
.position(CGPoint(x: environment.safeInsets.left + 16.0 + closeButton.size.width / 2.0, y: 36.0))
|
||||
)
|
||||
|
||||
let title = title.update(
|
||||
|
|
@ -234,6 +242,7 @@ private final class SheetContent: CombinedComponent {
|
|||
let button = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
|
|
@ -256,7 +265,7 @@ private final class SheetContent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(button
|
||||
|
|
@ -335,6 +344,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
})
|
||||
}
|
||||
)),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -370,7 +370,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||
)
|
||||
))
|
||||
|
||||
let valueString = "⭐️\(formatStarsAmountText(StarsAmount(value: component.valueInfo.initialSaleStars, nanos: 0), dateTimeFormat: dateTimeFormat)) (≈\(formatCurrencyAmount(component.valueInfo.initialSalePrice, currency: component.valueInfo.currency)))"
|
||||
let valueString = "⭐️\(formatStarsAmountText(StarsAmount(value: component.valueInfo.initialSaleStars, nanos: 0), dateTimeFormat: dateTimeFormat)) (~\(formatCurrencyAmount(component.valueInfo.initialSalePrice, currency: component.valueInfo.currency)))"
|
||||
let valueAttributedString = NSMutableAttributedString(string: valueString, font: tableFont, textColor: tableTextColor)
|
||||
let range = (valueAttributedString.string as NSString).range(of: "⭐️")
|
||||
if range.location != NSNotFound {
|
||||
|
|
|
|||
|
|
@ -1438,7 +1438,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
})))
|
||||
|
||||
//TODO:localize
|
||||
if case let .unique(uniqueGift) = arguments.gift, case let .peerId(ownerPeerId) = uniqueGift.owner, ownerPeerId != self.context.account.peerId {
|
||||
if case let .unique(uniqueGift) = arguments.gift, case let .peerId(ownerPeerId) = uniqueGift.owner, ownerPeerId != self.context.account.peerId, uniqueGift.minOfferStars != nil {
|
||||
items.append(.action(ContextMenuActionItem(text: "Offer to Buy", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Paid"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
|
|
@ -2468,7 +2468,6 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
|
||||
let wearAvatar = Child(AvatarComponent.self)
|
||||
let wearPeerName = Child(MultilineTextComponent.self)
|
||||
let wearPeerStatus = Child(MultilineTextComponent.self)
|
||||
let wearTitle = Child(MultilineTextComponent.self)
|
||||
let wearDescription = Child(MultilineTextComponent.self)
|
||||
let wearPerks = Child(List<Empty>.self)
|
||||
|
|
@ -2525,6 +2524,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
var exported = false
|
||||
var canUpgrade = false
|
||||
var upgradeStars: Int64?
|
||||
var genericGift: StarGift.Gift?
|
||||
var uniqueGift: StarGift.UniqueGift?
|
||||
var isSelfGift = false
|
||||
var isChannelGift = false
|
||||
|
|
@ -2555,7 +2555,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
subtitleString = strings.Gift_View_ReleasedBy("[@\(addressName)]()").string
|
||||
releasedByPeer = peer
|
||||
}
|
||||
|
||||
genericGift = gift
|
||||
animationFile = gift.file
|
||||
stars = gift.price
|
||||
text = arguments.text
|
||||
|
|
@ -2690,6 +2690,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
} else if case let .upgradePreview(attributes, _) = component.subject {
|
||||
headerHeight = 258.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if case let .wearPreview(_, attributes) = component.subject, let attributes {
|
||||
headerHeight = 200.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if let animationFile {
|
||||
headerHeight = 210.0
|
||||
headerSubject = .generic(animationFile)
|
||||
|
|
@ -2709,7 +2712,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
let wearOwnerPeerId = ownerPeerId ?? component.context.account.peerId
|
||||
|
||||
var wearPeerNameChild: _UpdatedChildComponent?
|
||||
if showWearPreview, let uniqueGift {
|
||||
if showWearPreview {
|
||||
var peerName = ""
|
||||
if let ownerPeer = state.peerMap[wearOwnerPeerId] {
|
||||
peerName = ownerPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
|
|
@ -2718,7 +2721,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: peerName,
|
||||
font: Font.bold(28.0),
|
||||
font: Font.bold(20.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
|
|
@ -2730,10 +2733,20 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
)
|
||||
|
||||
let giftTitle: String
|
||||
if case .wearPreview = component.subject {
|
||||
giftTitle = uniqueGift.title
|
||||
if let uniqueGift {
|
||||
if case .wearPreview = component.subject {
|
||||
giftTitle = uniqueGift.title
|
||||
} else {
|
||||
giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))"
|
||||
}
|
||||
} else if let genericGift {
|
||||
if let number = component.subject.arguments?.giftNumber {
|
||||
giftTitle = "\(genericGift.title ?? "") #\(formatCollectibleNumber(number, dateTimeFormat: environment.dateTimeFormat))"
|
||||
} else {
|
||||
giftTitle = genericGift.title ?? ""
|
||||
}
|
||||
} else {
|
||||
giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))"
|
||||
giftTitle = ""
|
||||
}
|
||||
|
||||
let wearTitle = wearTitle.update(
|
||||
|
|
@ -2766,14 +2779,14 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
transition: .immediate
|
||||
)
|
||||
|
||||
var titleOriginY = headerHeight + 18.0
|
||||
var titleOriginY = headerHeight + 10.0
|
||||
context.add(wearTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: titleOriginY + wearTitle.size.height))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
titleOriginY += wearTitle.size.height
|
||||
titleOriginY += 18.0
|
||||
titleOriginY += 10.0
|
||||
|
||||
context.add(wearDescription
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: titleOriginY + wearDescription.size.height))
|
||||
|
|
@ -2785,7 +2798,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
var animationOffset: CGPoint?
|
||||
var animationScale: CGFloat?
|
||||
if let wearPeerNameChild {
|
||||
animationOffset = CGPoint(x: wearPeerNameChild.size.width / 2.0 + 20.0 - 12.0, y: 56.0)
|
||||
animationOffset = CGPoint(x: wearPeerNameChild.size.width / 2.0 + 20.0 - 12.0, y: 79.0)
|
||||
animationScale = 0.19
|
||||
}
|
||||
|
||||
|
|
@ -2848,42 +2861,21 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
)
|
||||
headerComponents.append({
|
||||
context.add(wearAvatar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 86.0))
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let wearPeerStatus = wearPeerStatus.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: isChannelGift ? strings.Channel_Status : strings.Presence_online,
|
||||
font: Font.regular(17.0),
|
||||
textColor: vibrantColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 5,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
|
||||
headerComponents.append({
|
||||
context.add(wearPeerNameChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 144.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
context.add(wearPeerStatus
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 174.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 167.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
})
|
||||
originY += 108.0
|
||||
originY += 91.0
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
|
|
@ -4207,7 +4199,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "≈\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor))))
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor))))
|
||||
),
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
|
|
@ -5307,7 +5299,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
case profileGift(EnginePeer.Id, ProfileGiftsContext.State.StarGift)
|
||||
case soldOutGift(StarGift.Gift)
|
||||
case upgradePreview([StarGift.UniqueGift.Attribute], String)
|
||||
case wearPreview(StarGift.UniqueGift)
|
||||
case wearPreview(StarGift, [StarGift.UniqueGift.Attribute]?)
|
||||
|
||||
var arguments: (
|
||||
peerId: EnginePeer.Id?,
|
||||
|
|
@ -5339,13 +5331,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
prepaidUpgradeHash: String?,
|
||||
upgradeSeparate: Bool,
|
||||
dropOriginalDetailsStars: Int64?,
|
||||
auctionToPeerId: EnginePeer.Id?
|
||||
auctionToPeerId: EnginePeer.Id?,
|
||||
giftNumber: Int32?
|
||||
)? {
|
||||
switch self {
|
||||
case let .message(message):
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate, _, toPeerId):
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate, _, toPeerId, number):
|
||||
var reference: StarGiftReference
|
||||
if let peerId, let giftMessageId {
|
||||
reference = .message(messageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: giftMessageId))
|
||||
|
|
@ -5356,7 +5349,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
}
|
||||
|
||||
let fromPeerId = senderId ?? message.author?.id
|
||||
return (message.id.peerId, fromPeerId, message.author?.debugDisplayTitle, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil, prepaidUpgradeHash, upgradeSeparate, nil, toPeerId)
|
||||
return (message.id.peerId, fromPeerId, message.author?.debugDisplayTitle, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil, prepaidUpgradeHash, upgradeSeparate, nil, toPeerId, number)
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, _, peerId, senderId, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _):
|
||||
var reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
|
|
@ -5378,16 +5371,24 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
}
|
||||
|
||||
var resellAmounts: [CurrencyAmount]?
|
||||
var number: Int32?
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
resellAmounts = uniqueGift.resellAmounts
|
||||
number = uniqueGift.number
|
||||
}
|
||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.debugDisplayTitle, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate, nil, false, dropOriginalDetailsStars, nil, number)
|
||||
case let .starGiftPurchaseOffer(gift, _, _, _, _), let .starGiftPurchaseOfferDeclined(gift, _, _):
|
||||
if case let .unique(gift) = gift {
|
||||
return (nil, nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil, false, nil, nil, nil)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.debugDisplayTitle, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate, nil, false, dropOriginalDetailsStars, nil)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case let .uniqueGift(gift, _), let .wearPreview(gift):
|
||||
return (nil, nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil, false, nil, nil)
|
||||
case let .uniqueGift(gift, _):
|
||||
return (nil, nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil, false, nil, nil, gift.number)
|
||||
case let .profileGift(peerId, gift):
|
||||
var messageId: EngineMessage.Id?
|
||||
if case let .message(messageIdValue) = gift.reference {
|
||||
|
|
@ -5397,11 +5398,13 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
if case let .unique(uniqueGift) = gift.gift {
|
||||
resellAmounts = uniqueGift.resellAmounts
|
||||
}
|
||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.debugDisplayTitle, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash, gift.upgradeSeparate, gift.dropOriginalDetailsStars, nil)
|
||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.debugDisplayTitle, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash, gift.upgradeSeparate, gift.dropOriginalDetailsStars, nil, gift.number)
|
||||
case .soldOutGift:
|
||||
return nil
|
||||
case .upgradePreview:
|
||||
return nil
|
||||
case let .wearPreview(gift, _):
|
||||
return (nil, nil, nil, nil, nil, nil, false, gift, 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, nil, nil, nil, nil, nil, nil, false, nil, nil, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -6338,7 +6341,7 @@ private final class HeaderButtonComponent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
|
||||
private final class AvatarComponent: Component {
|
||||
final class AvatarComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let peer: EnginePeer
|
||||
|
|
|
|||
|
|
@ -53,10 +53,12 @@ final class TableComponent: CombinedComponent {
|
|||
|
||||
private let theme: PresentationTheme
|
||||
private let items: [Item]
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item]) {
|
||||
private let semiTransparent: Bool
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item], semiTransparent: Bool = false) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
self.semiTransparent = semiTransparent
|
||||
}
|
||||
|
||||
public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool {
|
||||
|
|
@ -66,6 +68,9 @@ final class TableComponent: CombinedComponent {
|
|||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.semiTransparent != rhs.semiTransparent {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +100,10 @@ final class TableComponent: CombinedComponent {
|
|||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
let secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
|
||||
var secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
|
||||
if context.component.semiTransparent {
|
||||
secondaryBackgroundColor = borderColor.withMultipliedAlpha(0.5)
|
||||
}
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/MediaAssetsContext",
|
||||
"//submodules/CheckNode",
|
||||
"//submodules/TelegramNotices",
|
||||
"//submodules/TelegramUI/Components/AttachmentFileController",
|
||||
"//submodules/SaveToCameraRoll",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ import UIKitRuntimeUtils
|
|||
import ImageObjectSeparation
|
||||
import SaveProgressScreen
|
||||
import TelegramNotices
|
||||
import AttachmentFileController
|
||||
import SaveToCameraRoll
|
||||
|
||||
private let playbackButtonTag = GenericComponentViewTag()
|
||||
private let muteButtonTag = GenericComponentViewTag()
|
||||
|
|
@ -5107,8 +5109,65 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}
|
||||
controller.push(locationController)
|
||||
}
|
||||
|
||||
|
||||
func presentAudioPicker() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
|
||||
let audioController = storyAudioPickerController(
|
||||
context: self.context,
|
||||
selectFromFiles: { [weak self] in
|
||||
self?.presentAudioFilePicker()
|
||||
},
|
||||
dismissed: { [weak self] in
|
||||
if let self {
|
||||
self.mediaEditor?.play()
|
||||
}
|
||||
},
|
||||
completion: { [weak self] file in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = (fetchMediaData(
|
||||
context: self.context,
|
||||
postbox: self.context.account.postbox,
|
||||
userLocation: .other,
|
||||
mediaReference: file
|
||||
) |> deliverOnMainQueue).start(next: { [weak self] state, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if case let .data(data) = state {
|
||||
let path = data.path
|
||||
|
||||
try? FileManager.default.createDirectory(atPath: draftPath(engine: self.context.engine), withIntermediateDirectories: true)
|
||||
|
||||
var originalFileName: String = "audio_\(Int.random(in: 0 ..< .max)).mp3"
|
||||
if let file = file.media as? TelegramMediaFile {
|
||||
originalFileName = file.fileName ?? "\(file.fileId.id).mp3"
|
||||
}
|
||||
|
||||
let fileName = "audio_\(originalFileName)"
|
||||
let copyPath = fullDraftPath(peerId: self.context.account.peerId, path: fileName)
|
||||
|
||||
try? FileManager.default.removeItem(atPath: copyPath)
|
||||
do {
|
||||
try FileManager.default.copyItem(atPath: path, toPath: copyPath)
|
||||
} catch let e {
|
||||
Logger.shared.log("MediaEditor", "copy file error \(e)")
|
||||
return
|
||||
}
|
||||
|
||||
self.insertAudio(path: copyPath, fileName: fileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
controller.push(audioController)
|
||||
}
|
||||
|
||||
func presentAudioFilePicker() {
|
||||
var isSettingTrack = false
|
||||
self.controller?.present(legacyICloudFilePicker(theme: self.presentationData.theme, mode: .import, documentTypes: ["public.mp3", "public.mpeg-4-audio", "public.aac-audio", "org.xiph.flac"], forceDarkTheme: true, dismissed: { [weak self] in
|
||||
if let self {
|
||||
|
|
@ -5119,7 +5178,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}
|
||||
}
|
||||
}, completion: { [weak self] urls in
|
||||
guard let self, let mediaEditor = self.mediaEditor, !urls.isEmpty, let url = urls.first else {
|
||||
guard let self, !urls.isEmpty, let url = urls.first else {
|
||||
return
|
||||
}
|
||||
isSettingTrack = true
|
||||
|
|
@ -5132,7 +5191,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
let coordinator = NSFileCoordinator(filePresenter: nil)
|
||||
var error: NSError?
|
||||
coordinator.coordinate(readingItemAt: url, options: .forUploading, error: &error, byAccessor: { sourceUrl in
|
||||
let fileName = "audio_\(sourceUrl.lastPathComponent)"
|
||||
let fileName = "audio_\(sourceUrl.lastPathComponent)"
|
||||
let copyPath = fullDraftPath(peerId: self.context.account.peerId, path: fileName)
|
||||
|
||||
try? FileManager.default.removeItem(atPath: copyPath)
|
||||
|
|
@ -5147,92 +5206,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}
|
||||
|
||||
Queue.mainQueue().async {
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: copyPath))
|
||||
|
||||
func loadValues(asset: AVAsset, retryCount: Int, completion: @escaping () -> Void) {
|
||||
asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"], completionHandler: {
|
||||
if asset.statusOfValue(forKey: "tracks", error: nil) == .loading {
|
||||
if retryCount < 2 {
|
||||
Queue.mainQueue().after(0.1, {
|
||||
loadValues(asset: asset, retryCount: retryCount + 1, completion: completion)
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
loadValues(asset: audioAsset, retryCount: 0, completion: {
|
||||
var audioDuration: Double = 0.0
|
||||
guard let track = audioAsset.tracks(withMediaType: .audio).first else {
|
||||
Logger.shared.log("MediaEditor", "track is nil")
|
||||
if isScopedResource {
|
||||
url.stopAccessingSecurityScopedResource()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
audioDuration = track.timeRange.duration.seconds
|
||||
if audioDuration.isZero {
|
||||
Logger.shared.log("MediaEditor", "duration is zero")
|
||||
if isScopedResource {
|
||||
url.stopAccessingSecurityScopedResource()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func maybeFixMisencodedText(_ text: String) -> String {
|
||||
let charactersToSearchFor = CharacterSet(charactersIn: "àåèîóûþÿ")
|
||||
if text.lowercased().rangeOfCharacter(from: charactersToSearchFor) != nil {
|
||||
if let data = text.data(using: .windowsCP1252), let string = String(data: data, encoding: .windowsCP1251) {
|
||||
return string
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
var artist: String?
|
||||
var title: String?
|
||||
for data in audioAsset.commonMetadata {
|
||||
if data.commonKey == .commonKeyArtist, let value = data.stringValue {
|
||||
artist = maybeFixMisencodedText(value)
|
||||
}
|
||||
if data.commonKey == .commonKeyTitle, let value = data.stringValue {
|
||||
title = maybeFixMisencodedText(value)
|
||||
}
|
||||
}
|
||||
|
||||
Queue.mainQueue().async {
|
||||
var audioTrimRange: Range<Double>?
|
||||
var audioOffset: Double?
|
||||
|
||||
if let videoDuration = mediaEditor.originalCappedDuration {
|
||||
if let videoStart = mediaEditor.values.videoTrimRange?.lowerBound {
|
||||
audioOffset = -videoStart
|
||||
} else if let _ = mediaEditor.values.additionalVideoPath, let videoStart = mediaEditor.values.additionalVideoTrimRange?.lowerBound {
|
||||
audioOffset = -videoStart
|
||||
}
|
||||
audioTrimRange = 0 ..< min(videoDuration, audioDuration)
|
||||
} else {
|
||||
audioTrimRange = 0 ..< min(15, audioDuration)
|
||||
}
|
||||
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: fileName, artist: artist, title: title, duration: audioDuration), trimRange: audioTrimRange, offset: audioOffset)
|
||||
|
||||
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: true)
|
||||
|
||||
self.requestUpdate(transition: .easeInOut(duration: 0.2))
|
||||
if isScopedResource {
|
||||
url.stopAccessingSecurityScopedResource()
|
||||
}
|
||||
|
||||
mediaEditor.play()
|
||||
self.insertAudio(path: copyPath, fileName: fileName, dispose: {
|
||||
if isScopedResource {
|
||||
url.stopAccessingSecurityScopedResource()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -5244,6 +5220,94 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}), in: .window(.root))
|
||||
}
|
||||
|
||||
private func insertAudio(path: String, fileName: String, dispose: (() -> Void)? = nil) {
|
||||
guard let mediaEditor = self.mediaEditor else {
|
||||
return
|
||||
}
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: path))
|
||||
|
||||
func loadValues(asset: AVAsset, retryCount: Int, completion: @escaping () -> Void) {
|
||||
asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"], completionHandler: {
|
||||
if asset.statusOfValue(forKey: "tracks", error: nil) == .loading {
|
||||
if retryCount < 2 {
|
||||
Queue.mainQueue().after(0.1, {
|
||||
loadValues(asset: asset, retryCount: retryCount + 1, completion: completion)
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
loadValues(asset: audioAsset, retryCount: 0, completion: {
|
||||
var audioDuration: Double = 0.0
|
||||
guard let track = audioAsset.tracks(withMediaType: .audio).first else {
|
||||
Logger.shared.log("MediaEditor", "track is nil")
|
||||
dispose?()
|
||||
return
|
||||
}
|
||||
|
||||
audioDuration = track.timeRange.duration.seconds
|
||||
if audioDuration.isZero {
|
||||
Logger.shared.log("MediaEditor", "duration is zero")
|
||||
dispose?()
|
||||
return
|
||||
}
|
||||
|
||||
func maybeFixMisencodedText(_ text: String) -> String {
|
||||
let charactersToSearchFor = CharacterSet(charactersIn: "àåèîóûþÿ")
|
||||
if text.lowercased().rangeOfCharacter(from: charactersToSearchFor) != nil {
|
||||
if let data = text.data(using: .windowsCP1252), let string = String(data: data, encoding: .windowsCP1251) {
|
||||
return string
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
var artist: String?
|
||||
var title: String?
|
||||
for data in audioAsset.commonMetadata {
|
||||
if data.commonKey == .commonKeyArtist, let value = data.stringValue {
|
||||
artist = maybeFixMisencodedText(value)
|
||||
}
|
||||
if data.commonKey == .commonKeyTitle, let value = data.stringValue {
|
||||
title = maybeFixMisencodedText(value)
|
||||
}
|
||||
}
|
||||
|
||||
Queue.mainQueue().async {
|
||||
var audioTrimRange: Range<Double>?
|
||||
var audioOffset: Double?
|
||||
|
||||
if let videoDuration = mediaEditor.originalCappedDuration {
|
||||
if let videoStart = mediaEditor.values.videoTrimRange?.lowerBound {
|
||||
audioOffset = -videoStart
|
||||
} else if let _ = mediaEditor.values.additionalVideoPath, let videoStart = mediaEditor.values.additionalVideoTrimRange?.lowerBound {
|
||||
audioOffset = -videoStart
|
||||
}
|
||||
audioTrimRange = 0 ..< min(videoDuration, audioDuration)
|
||||
} else {
|
||||
audioTrimRange = 0 ..< min(15, audioDuration)
|
||||
}
|
||||
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: fileName, artist: artist, title: title, duration: audioDuration), trimRange: audioTrimRange, offset: audioOffset)
|
||||
|
||||
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: true)
|
||||
|
||||
self.requestUpdate(transition: .easeInOut(duration: 0.2))
|
||||
dispose?()
|
||||
|
||||
mediaEditor.play()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func presentVideoRemoveConfirmation() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
|||
var contentSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
|
||||
let usdRate = Double(component.usdWithdrawRate) / 1000.0 / 100.0
|
||||
let price = self.starCount == 0 ? "" : "≈\(formatTonUsdValue(Int64(self.starCount), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
let price = self.starCount == 0 ? "" : "~\(formatTonUsdValue(Int64(self.starCount), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
|
||||
contentSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
|
||||
itemGenerator: MessagePriceItem(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "SegmentControlComponent",
|
||||
module_name = "SegmentControlComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/SegmentedControlNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -7,10 +7,15 @@ import ComponentDisplayAdapters
|
|||
import TelegramPresentationData
|
||||
import SegmentedControlNode
|
||||
|
||||
final class SegmentControlComponent: Component {
|
||||
struct Item: Equatable {
|
||||
public final class SegmentControlComponent: Component {
|
||||
public struct Item: Equatable {
|
||||
var id: AnyHashable
|
||||
var title: String
|
||||
|
||||
public init(id: AnyHashable, title: String) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
}
|
||||
}
|
||||
|
||||
let theme: PresentationTheme
|
||||
|
|
@ -18,7 +23,7 @@ final class SegmentControlComponent: Component {
|
|||
let selectedId: AnyHashable?
|
||||
let action: (AnyHashable) -> Void
|
||||
|
||||
init(
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
items: [Item],
|
||||
selectedId: AnyHashable?,
|
||||
|
|
@ -30,7 +35,7 @@ final class SegmentControlComponent: Component {
|
|||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: SegmentControlComponent, rhs: SegmentControlComponent) -> Bool {
|
||||
public static func ==(lhs: SegmentControlComponent, rhs: SegmentControlComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
|
|
@ -80,7 +85,7 @@ final class SegmentControlComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
class View: UIView {
|
||||
public class View: UIView {
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
private var component: SegmentControlComponent?
|
||||
|
|
@ -88,7 +93,7 @@ final class SegmentControlComponent: Component {
|
|||
private var nativeSegmentedView: SegmentedControlView?
|
||||
private var legacySegmentedNode: SegmentedControlNode?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
|
|
@ -171,11 +176,11 @@ final class SegmentControlComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
@ -774,6 +774,7 @@ final class UserAppearanceScreenComponent: Component {
|
|||
releasedBy: nil,
|
||||
valueAmount: nil,
|
||||
valueCurrency: nil,
|
||||
valueUsdAmount: nil,
|
||||
flags: [],
|
||||
themePeerId: nil,
|
||||
peerColor: nil,
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ final class StarsBalanceComponent: Component {
|
|||
|
||||
let subtitleText: String
|
||||
if let rate = component.rate {
|
||||
subtitleText = "≈\(formatTonUsdValue(component.count.value, divide: false, rate: rate, dateTimeFormat: component.dateTimeFormat))"
|
||||
subtitleText = "~\(formatTonUsdValue(component.count.value, divide: false, rate: rate, dateTimeFormat: component.dateTimeFormat))"
|
||||
} else {
|
||||
subtitleText = component.strings.Stars_Intro_YourBalance
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ final class StarsOverviewItemComponent: Component {
|
|||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "≈\(usdValueString)", font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||
text: .plain(NSAttributedString(string: "~\(usdValueString)", font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
||||
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import TabSelectorComponent
|
|||
import PresentationDataUtils
|
||||
import BalanceNeededScreen
|
||||
import GlassBarButtonComponent
|
||||
import GlassBackgroundComponent
|
||||
|
||||
private let amountTag = GenericComponentViewTag()
|
||||
|
||||
|
|
@ -68,6 +69,7 @@ private final class SheetContent: CombinedComponent {
|
|||
let balanceTitle = Child(MultilineTextComponent.self)
|
||||
let balanceValue = Child(MultilineTextComponent.self)
|
||||
let balanceIcon = Child(BundleIconComponent.self)
|
||||
let durationPicker = Child(MenuComponent.self)
|
||||
|
||||
let body: (CombinedComponentContext<SheetContent>) -> CGSize = { (context: CombinedComponentContext<SheetContent>) -> CGSize in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
|
@ -356,6 +358,7 @@ private final class SheetContent: CombinedComponent {
|
|||
tonTitle = environment.strings.Chat_PostSuggestion_Suggest_RequestTon
|
||||
}
|
||||
case .starGiftOffer:
|
||||
//TODO:localize
|
||||
displayCurrencySelector = true
|
||||
starsTitle = "Offer Stars"
|
||||
tonTitle = "Offer TON"
|
||||
|
|
@ -625,6 +628,7 @@ private final class SheetContent: CombinedComponent {
|
|||
.position(CGPoint(x: context.availableSize.width - amountAdditionalLabel.size.width / 2.0 - sideInset - 16.0, y: contentSize.height - amountAdditionalLabel.size.height / 2.0)))
|
||||
}
|
||||
|
||||
var durationFrame = CGRect()
|
||||
if case .starGiftResell = component.mode {
|
||||
contentSize.height += 24.0
|
||||
|
||||
|
|
@ -808,6 +812,14 @@ private final class SheetContent: CombinedComponent {
|
|||
maximumNumberOfLines: 0
|
||||
))
|
||||
|
||||
let hours = state.duration / 3600
|
||||
let durationString: String
|
||||
if hours == 1 {
|
||||
durationString = "1 Hour"
|
||||
} else {
|
||||
durationString = "\(hours) Hours"
|
||||
}
|
||||
|
||||
let periodSection = periodSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
|
|
@ -831,15 +843,19 @@ private final class SheetContent: CombinedComponent {
|
|||
], alignment: .left, spacing: 2.0)),
|
||||
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "48 Hours",
|
||||
string: durationString,
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: environment.theme.list.itemSecondaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
)))),
|
||||
accessory: .expandArrows,
|
||||
action: { _ in
|
||||
|
||||
action: { [weak state] _ in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.isPickingDuration = true
|
||||
state.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
))
|
||||
)]
|
||||
|
|
@ -853,6 +869,8 @@ private final class SheetContent: CombinedComponent {
|
|||
.clipsToBounds(true)
|
||||
.cornerRadius(10.0)
|
||||
)
|
||||
durationFrame = CGRect(origin: CGPoint(x: context.availableSize.width / 2.0 - periodSection.size.width / 2.0, y: contentSize.height), size: periodSection.size)
|
||||
|
||||
contentSize.height += periodSection.size.height
|
||||
}
|
||||
|
||||
|
|
@ -1065,6 +1083,58 @@ private final class SheetContent: CombinedComponent {
|
|||
contentSize.height += buttonInsets.bottom
|
||||
}
|
||||
|
||||
|
||||
if state.isPickingDuration {
|
||||
let durationPicker = durationPicker.update(
|
||||
component: MenuComponent(
|
||||
theme: theme,
|
||||
sourceFrame: durationFrame.offsetBy(dx: 0.0, dy: 120.0),
|
||||
component: AnyComponent(DurationMenuComponent(
|
||||
theme: theme,
|
||||
strings: environment.strings,
|
||||
value: state.duration,
|
||||
valueUpdated: { [weak state] value in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.isPickingDuration = false
|
||||
state.duration = value
|
||||
|
||||
state.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
)),
|
||||
dismiss: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.isPickingDuration = false
|
||||
state.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
),
|
||||
availableSize: contentSize,
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(durationPicker
|
||||
.position(CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0))
|
||||
.appear(ComponentTransition.Appear({ _, view, transition in
|
||||
if !transition.animation.isImmediate {
|
||||
if let view = view as? MenuComponent.View {
|
||||
view.animateIn()
|
||||
}
|
||||
}
|
||||
}))
|
||||
.disappear(ComponentTransition.Disappear({ view, transition, completion in
|
||||
if !transition.animation.isImmediate {
|
||||
if let view = view as? MenuComponent.View {
|
||||
view.animateOut(completion: completion)
|
||||
}
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
return contentSize
|
||||
}
|
||||
|
||||
|
|
@ -1080,7 +1150,7 @@ private final class SheetContent: CombinedComponent {
|
|||
fileprivate var amount: StarsAmount?
|
||||
fileprivate var currency: CurrencyAmount.Currency = .stars
|
||||
fileprivate var timestamp: Int32?
|
||||
fileprivate var duration: Int32 = 120
|
||||
fileprivate var duration: Int32 = 172800
|
||||
|
||||
fileprivate var starsBalance: StarsAmount?
|
||||
private var starsStateDisposable: Disposable?
|
||||
|
|
@ -1091,6 +1161,8 @@ private final class SheetContent: CombinedComponent {
|
|||
var cachedTonImage: (UIImage, PresentationTheme)?
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
var isPickingDuration = false
|
||||
|
||||
init(component: SheetContent) {
|
||||
self.context = component.context
|
||||
self.mode = component.mode
|
||||
|
|
@ -2251,3 +2323,423 @@ private final class CurrencyTabItemComponent: Component {
|
|||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final class MenuComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let sourceFrame: CGRect
|
||||
let component: AnyComponent<Empty>
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
sourceFrame: CGRect,
|
||||
component: AnyComponent<Empty>,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.sourceFrame = sourceFrame
|
||||
self.component = component
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
public static func ==(lhs: MenuComponent, rhs: MenuComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.sourceFrame != rhs.sourceFrame {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let buttonView: UIButton
|
||||
private let containerView: GlassBackgroundContainerView
|
||||
private let backgroundView: GlassBackgroundView
|
||||
private var componentView: ComponentView<Empty>?
|
||||
|
||||
private var component: MenuComponent?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.buttonView = UIButton()
|
||||
self.containerView = GlassBackgroundContainerView()
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.buttonView)
|
||||
self.addSubview(self.containerView)
|
||||
self.containerView.contentView.addSubview(self.backgroundView)
|
||||
|
||||
self.buttonView.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func tapped() {
|
||||
if let component = self.component {
|
||||
component.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
let transition = ComponentTransition.spring(duration: 0.3)
|
||||
transition.animatePosition(view: self.backgroundView, from: component.sourceFrame.center, to: self.backgroundView.center)
|
||||
transition.animateScale(view: self.backgroundView, from: 0.2, to: 1.0)
|
||||
self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
}
|
||||
|
||||
public func animateOut(completion: (() -> Void)? = nil) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let transition = ComponentTransition.spring(duration: 0.3)
|
||||
transition.setPosition(view: self.backgroundView, position: component.sourceFrame.center)
|
||||
transition.setScale(view: self.backgroundView, scale: 0.2)
|
||||
self.containerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.backgroundView.frame.contains(point) && self.buttonView.frame.contains(point) {
|
||||
return self.buttonView
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
func update(component: MenuComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
var componentView: ComponentView<Empty>
|
||||
var componentTransition = transition
|
||||
if let current = self.componentView {
|
||||
componentView = current
|
||||
} else {
|
||||
componentTransition = .immediate
|
||||
componentView = ComponentView()
|
||||
self.componentView = componentView
|
||||
}
|
||||
|
||||
let componentSize = componentView.update(
|
||||
transition: componentTransition,
|
||||
component: component.component,
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: component.sourceFrame.maxX - componentSize.width, y: component.sourceFrame.minY - componentSize.height - 20.0), size: componentSize)
|
||||
if let view = componentView.view {
|
||||
if view.superview == nil {
|
||||
self.backgroundView.contentView.addSubview(view)
|
||||
}
|
||||
componentTransition.setFrame(view: view, frame: CGRect(origin: .zero, size: componentSize))
|
||||
}
|
||||
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: 30.0, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: component.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition)
|
||||
self.backgroundView.frame = backgroundFrame
|
||||
|
||||
self.containerView.frame = CGRect(origin: .zero, size: availableSize)
|
||||
self.containerView.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition)
|
||||
|
||||
self.buttonView.frame = CGRect(origin: .zero, size: availableSize)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MenuButtonComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let text: String
|
||||
let isSelected: Bool
|
||||
let width: CGFloat?
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
text: String,
|
||||
isSelected: Bool,
|
||||
width: CGFloat?,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.text = text
|
||||
self.isSelected = isSelected
|
||||
self.width = width
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: MenuButtonComponent, rhs: MenuButtonComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
if lhs.isSelected != rhs.isSelected {
|
||||
return false
|
||||
}
|
||||
if lhs.width != rhs.width {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var component: MenuButtonComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
private let selectionLayer = SimpleLayer()
|
||||
private let title = ComponentView<Empty>()
|
||||
private let icon = ComponentView<Empty>()
|
||||
private let button = HighlightTrackingButton()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.layer.addSublayer(self.selectionLayer)
|
||||
self.selectionLayer.masksToBounds = true
|
||||
self.selectionLayer.opacity = 0.0
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let self {
|
||||
if highlighted {
|
||||
self.selectionLayer.opacity = 1.0
|
||||
self.selectionLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
} else {
|
||||
self.selectionLayer.opacity = 0.0
|
||||
self.selectionLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
if let component = self.component {
|
||||
component.action()
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: MenuButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.componentState = state
|
||||
|
||||
let leftInset: CGFloat = 60.0
|
||||
let rightInset: CGFloat = 40.0
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: component.text, font: Font.regular(17.0), color: component.theme.contextMenu.primaryColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: 60.0, y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
let size = CGSize(width: component.width ?? (leftInset + rightInset + titleSize.width), height: availableSize.height)
|
||||
|
||||
if component.isSelected {
|
||||
let iconSize = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Media Gallery/Check",
|
||||
tintColor: component.theme.contextMenu.primaryColor
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: 25.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.addSubview(iconView)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
}
|
||||
|
||||
self.selectionLayer.backgroundColor = component.theme.contextMenu.itemHighlightedBackgroundColor.withMultipliedAlpha(0.5).cgColor
|
||||
transition.setFrame(layer: self.selectionLayer, frame: CGRect(origin: .zero, size: size).insetBy(dx: 10.0, dy: 0.0))
|
||||
self.selectionLayer.cornerRadius = size.height / 2.0
|
||||
|
||||
if self.button.superview == nil {
|
||||
self.addSubview(self.button)
|
||||
}
|
||||
self.button.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class DurationMenuComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let value: Int32
|
||||
let valueUpdated: (Int32) -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
value: Int32,
|
||||
valueUpdated: @escaping (Int32) -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.value = value
|
||||
self.valueUpdated = valueUpdated
|
||||
}
|
||||
|
||||
public static func ==(lhs: DurationMenuComponent, rhs: DurationMenuComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.value != rhs.value {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let backgroundView: GlassBackgroundView
|
||||
private var itemViews: [Int32: ComponentView<Empty>] = [:]
|
||||
|
||||
private var component: DurationMenuComponent?
|
||||
|
||||
private let values: [Int32] = [
|
||||
21600, 43200, 86400, 129600, 172800, 259200
|
||||
]
|
||||
|
||||
private var width: CGFloat?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: DurationMenuComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let itemHeight: CGFloat = 40.0
|
||||
|
||||
var maxWidth: CGFloat = 0.0
|
||||
var originY: CGFloat = 12.0
|
||||
for value in self.values {
|
||||
let itemView: ComponentView<Empty>
|
||||
if let current = self.itemViews[value] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemView = ComponentView()
|
||||
self.itemViews[value] = itemView
|
||||
}
|
||||
|
||||
let repeatString: String
|
||||
let hours = value / 3600
|
||||
//TODO:localize
|
||||
if hours == 1 {
|
||||
repeatString = "1 hour"
|
||||
} else {
|
||||
repeatString = "\(hours) hours"
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MenuButtonComponent(
|
||||
theme: component.theme,
|
||||
text: repeatString,
|
||||
isSelected: component.value == value,
|
||||
width: self.width,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.component?.valueUpdated(value)
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: itemHeight)
|
||||
)
|
||||
maxWidth = max(maxWidth, itemSize.width)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: originY), size: itemSize)
|
||||
if let itemView = itemView.view {
|
||||
if itemView.superview == nil {
|
||||
self.addSubview(itemView)
|
||||
}
|
||||
transition.setFrame(view: itemView, frame: itemFrame)
|
||||
}
|
||||
originY += 40.0
|
||||
}
|
||||
|
||||
let size = CGSize(width: maxWidth, height: originY + 8.0)
|
||||
|
||||
if self.width == nil {
|
||||
self.width = maxWidth
|
||||
Queue.mainQueue().justDispatch {
|
||||
state.updated()
|
||||
}
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/BottomButtonPanelComponent",
|
||||
"//submodules/TelegramUI/Components/SliderComponent",
|
||||
"//submodules/TelegramUI/Components/SegmentControlComponent",
|
||||
"//submodules/CheckNode",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/ContextUI",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import TelegramStringFormatting
|
|||
import GalleryData
|
||||
import AnimatedTextComponent
|
||||
import TelegramUIPreferences
|
||||
import SegmentControlComponent
|
||||
|
||||
final class DataUsageScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
|
|||
|
|
@ -1328,6 +1328,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
case .paidMessagesPriceEdited:
|
||||
self.interfaceInteraction?.openMonoforum()
|
||||
return true
|
||||
case .starGiftPurchaseOffer:
|
||||
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let self {
|
||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||
self.push(controller)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.push(controller)
|
||||
return true
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -2401,7 +2412,44 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return
|
||||
}
|
||||
|
||||
if let attribute = message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute {
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGiftPurchaseOffer(gift, amount, _, isAccepted, isDeclined) = action.action, !isAccepted && !isDeclined {
|
||||
guard let data = data?.makeData(), message.effectivelyIncoming(strongSelf.context.account.peerId) else {
|
||||
return
|
||||
}
|
||||
if data.count != 1 {
|
||||
return
|
||||
}
|
||||
let buttonType = data.withUnsafeBytes { buffer -> UInt8 in
|
||||
return buffer.baseAddress!.assumingMemoryBound(to: UInt8.self).pointee
|
||||
}
|
||||
|
||||
switch buttonType {
|
||||
case 0:
|
||||
let _ = strongSelf.context.engine.payments.resolveStarGiftOffer(messageId: message.id, accept: false).startStandalone()
|
||||
case 1:
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer, case let .unique(gift) = gift else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeGiftOfferScreen(
|
||||
context: self.context,
|
||||
gift: gift,
|
||||
peer: peer,
|
||||
amount: amount,
|
||||
commit: { [weak self] in
|
||||
if let self {
|
||||
let _ = self.context.engine.payments.resolveStarGiftOffer(messageId: message.id, accept: true).startStandalone()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.present(controller, in: .window(.root))
|
||||
})
|
||||
default:
|
||||
break
|
||||
}
|
||||
return
|
||||
} else if let attribute = message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute {
|
||||
guard let data = data?.makeData() else {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -939,13 +939,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||
context?.account.viewTracker.refreshStoriesForMessageIds(messageIds: Set(messageIds.map(\.messageId)))
|
||||
}
|
||||
self.translationProcessingManager.process = { [weak self, weak context] messageIds in
|
||||
if let context = context, let translationLang = self?.translationLang {
|
||||
if let context, let translationLang = self?.translationLang {
|
||||
let _ = translateMessageIds(context: context, messageIds: Array(messageIds.map(\.messageId)), fromLang: translationLang.fromLang, toLang: translationLang.toLang).startStandalone()
|
||||
}
|
||||
}
|
||||
self.factCheckProcessingManager.process = { [weak self, weak context] messageIds in
|
||||
if let context = context, let translationLang = self?.translationLang {
|
||||
let _ = translateMessageIds(context: context, messageIds: Array(messageIds.map(\.messageId)), fromLang: translationLang.fromLang, toLang: translationLang.toLang).startStandalone()
|
||||
self.factCheckProcessingManager.process = { [weak context] messageIds in
|
||||
if let context {
|
||||
let _ = context.engine.messages.getMessagesFactCheck(messageIds: Array(messageIds.map(\.messageId))).startStandalone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2746,6 +2746,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||
messageIdsToTranslate.append(message.id)
|
||||
} else if let _ = message.media.first(where: { $0 is TelegramMediaPoll }) {
|
||||
messageIdsToTranslate.append(message.id)
|
||||
} else if let audioTranscription = message.attributes.first(where: { $0 is AudioTranscriptionMessageAttribute }) as? AudioTranscriptionMessageAttribute, !audioTranscription.text.isEmpty && !audioTranscription.isPending {
|
||||
messageIdsToTranslate.append(message.id)
|
||||
}
|
||||
case let .MessageGroupEntry(_, messages, _):
|
||||
for (message, _, _, _, _) in messages {
|
||||
|
|
|
|||
|
|
@ -3839,9 +3839,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return controller
|
||||
}
|
||||
|
||||
public func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController {
|
||||
let controller = GiftViewScreen(context: context, subject: .wearPreview(gift))
|
||||
return controller
|
||||
public func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]?) -> ViewController {
|
||||
return GiftViewScreen(context: context, subject: .wearPreview(gift, attributes))
|
||||
}
|
||||
|
||||
public func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController {
|
||||
return GiftViewScreen(context: context, subject: .upgradePreview(attributes, peerName))
|
||||
}
|
||||
|
||||
public func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController {
|
||||
|
|
@ -3860,6 +3863,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return GiftAuctionActiveBidsScreen(context: context)
|
||||
}
|
||||
|
||||
public func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController {
|
||||
return giftOfferAlertController(context: context, gift: gift, peer: peer, amount: amount, commit: commit)
|
||||
}
|
||||
|
||||
public func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController {
|
||||
let editorSubject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||
switch subject {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue