Various improvements

This commit is contained in:
Ilya Laktyushin 2024-05-24 02:43:22 +04:00
parent 4e55e3aab0
commit 2007f15998
23 changed files with 383 additions and 158 deletions

View File

@ -196,7 +196,16 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition)
let component = AnyComponent(PremiumStarComponent(isIntro: true, isVisible: true, hasIdleAnimations: true))
let component = AnyComponent(PremiumStarComponent(
isIntro: true,
isVisible: true,
hasIdleAnimations: true,
colors: [
UIColor(rgb: 0x6a94ff),
UIColor(rgb: 0x9472fd),
UIColor(rgb: 0xe26bd3)
]
))
let containerSize = CGSize(width: min(414.0, layout.size.width), height: 220.0)
if let hostView = self.hostView {

View File

@ -266,7 +266,16 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
)
let star = star.update(
component: PremiumStarComponent(isIntro: false, isVisible: true, hasIdleAnimations: true),
component: PremiumStarComponent(
isIntro: false,
isVisible: true,
hasIdleAnimations: true,
colors: [
UIColor(rgb: 0x6a94ff),
UIColor(rgb: 0x9472fd),
UIColor(rgb: 0xe26bd3)
]
),
availableSize: CGSize(width: context.availableSize.width, height: 200.0),
transition: .immediate
)

View File

@ -3212,7 +3212,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
component: PremiumStarComponent(
isIntro: isIntro,
isVisible: starIsVisible,
hasIdleAnimations: state.hasIdleAnimations
hasIdleAnimations: state.hasIdleAnimations,
colors: [
UIColor(rgb: 0x6a94ff),
UIColor(rgb: 0x9472fd),
UIColor(rgb: 0xe26bd3)
]
),
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
transition: context.transition

View File

@ -870,7 +870,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }
dict[1600878860] = { return Api.StarsTransaction.parse_starsTransaction($0) }
dict[-865044046] = { return Api.StarsTransaction.parse_starsTransaction($0) }
dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) }
dict[-1269320843] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAppStore($0) }
dict[-382740222] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerFragment($0) }
@ -1302,6 +1302,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
dict[2079764828] = { return Api.payments.PaymentForm.parse_paymentFormStars($0) }
dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
dict[-319617530] = { return Api.payments.PaymentReceipt.parse_paymentReceiptStars($0) }
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
@ -1379,7 +1380,7 @@ public extension Api {
return parser(reader)
}
else {
telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found")
telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found")
return nil
}
}

View File

@ -656,46 +656,64 @@ public extension Api {
}
public extension Api {
enum StarsTransaction: TypeConstructorDescription {
case starsTransaction(id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer)
case starsTransaction(flags: Int32, id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .starsTransaction(let id, let stars, let date, let peer):
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo):
if boxed {
buffer.appendInt32(1600878860)
buffer.appendInt32(-865044046)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(id, buffer: buffer, boxed: false)
serializeInt64(stars, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(description!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .starsTransaction(let id, let stars, let date, let peer):
return ("starsTransaction", [("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any)])
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo):
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any)])
}
}
public static func parse_starsTransaction(_ reader: BufferReader) -> StarsTransaction? {
var _1: String?
_1 = parseString(reader)
var _2: Int64?
_2 = reader.readInt64()
var _3: Int32?
_3 = reader.readInt32()
var _4: Api.StarsTransactionPeer?
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Int64?
_3 = reader.readInt64()
var _4: Int32?
_4 = reader.readInt32()
var _5: Api.StarsTransactionPeer?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.StarsTransactionPeer
_5 = Api.parse(reader, signature: signature) as? Api.StarsTransactionPeer
}
var _6: String?
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
var _7: String?
if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) }
var _8: Api.WebDocument?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.WebDocument
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.StarsTransaction.starsTransaction(id: _1!, stars: _2!, date: _3!, peer: _4!)
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8)
}
else {
return nil

View File

@ -771,6 +771,7 @@ public extension Api.payments {
public extension Api.payments {
enum PaymentReceipt: TypeConstructorDescription {
case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User])
case paymentReceiptStars(flags: Int32, date: Int32, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, currency: String, totalAmount: Int64, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -798,6 +799,25 @@ public extension Api.payments {
item.serialize(buffer, true)
}
break
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let users):
if boxed {
buffer.appendInt32(-319617530)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt64(botId, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
invoice.serialize(buffer, true)
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(totalAmount, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
@ -805,6 +825,8 @@ public extension Api.payments {
switch self {
case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users):
return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)])
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let users):
return ("paymentReceiptStars", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("users", users as Any)])
}
}
@ -871,6 +893,50 @@ public extension Api.payments {
return nil
}
}
public static func parse_paymentReceiptStars(_ reader: BufferReader) -> PaymentReceipt? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int64?
_3 = reader.readInt64()
var _4: String?
_4 = parseString(reader)
var _5: String?
_5 = parseString(reader)
var _6: Api.WebDocument?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.WebDocument
} }
var _7: Api.Invoice?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.Invoice
}
var _8: String?
_8 = parseString(reader)
var _9: Int64?
_9 = reader.readInt64()
var _10: [Api.User]?
if let _ = reader.readInt32() {
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
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 << 2) == 0) || _6 != nil
let _c7 = _7 != nil
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.payments.PaymentReceipt.paymentReceiptStars(flags: _1!, date: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, currency: _8!, totalAmount: _9!, users: _10!)
}
else {
return nil
}
}
}
}
@ -1612,67 +1678,3 @@ public extension Api.photos {
}
}
public extension Api.premium {
enum BoostsList: TypeConstructorDescription {
case boostsList(flags: Int32, count: Int32, boosts: [Api.Boost], nextOffset: String?, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .boostsList(let flags, let count, let boosts, let nextOffset, let users):
if boxed {
buffer.appendInt32(-2030542532)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(boosts.count))
for item in boosts {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
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 .boostsList(let flags, let count, let boosts, let nextOffset, let users):
return ("boostsList", [("flags", flags as Any), ("count", count as Any), ("boosts", boosts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)])
}
}
public static func parse_boostsList(_ reader: BufferReader) -> BoostsList? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.Boost]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Boost.self)
}
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: [Api.User]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,67 @@
public extension Api.premium {
enum BoostsList: TypeConstructorDescription {
case boostsList(flags: Int32, count: Int32, boosts: [Api.Boost], nextOffset: String?, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .boostsList(let flags, let count, let boosts, let nextOffset, let users):
if boxed {
buffer.appendInt32(-2030542532)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(boosts.count))
for item in boosts {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
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 .boostsList(let flags, let count, let boosts, let nextOffset, let users):
return ("boostsList", [("flags", flags as Any), ("count", count as Any), ("boosts", boosts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)])
}
}
public static func parse_boostsList(_ reader: BufferReader) -> BoostsList? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.Boost]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Boost.self)
}
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: [Api.User]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!)
}
else {
return nil
}
}
}
}
public extension Api.premium {
enum BoostsStatus: TypeConstructorDescription {
case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, prepaidGiveaways: [Api.PrepaidGiveaway]?, myBoostSlots: [Int32]?)

View File

@ -8794,6 +8794,23 @@ public extension Api.functions.payments {
})
}
}
public extension Api.functions.payments {
static func refundStarsCharge(userId: Api.InputUser, msgId: Int32, chargeId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-258950164)
userId.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeString(chargeId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.refundStarsCharge", parameters: [("userId", String(describing: userId)), ("msgId", String(describing: msgId)), ("chargeId", String(describing: chargeId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
}
public extension Api.functions.payments {
static func sendPaymentForm(flags: Int32, formId: Int64, invoice: Api.InputInvoice, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials, tipAmount: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentResult>) {
let buffer = Buffer()

View File

@ -693,6 +693,27 @@ func _internal_requestBotPaymentReceipt(account: Account, messageId: MessageId)
let botPaymentId = PeerId.init(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId))
return BotPaymentReceipt(invoice: parsedInvoice, info: parsedInfo, shippingOption: shippingOption, credentialsTitle: credentialsTitle, invoiceMedia: invoiceMedia, tipAmount: tipAmount, botPaymentId: botPaymentId)
case let .paymentReceiptStars(_, _, botId, title, description, photo, invoice, currency, totalAmount, users: users):
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
let invoiceMedia = TelegramMediaInvoice(
title: title,
description: description,
photo: photo.flatMap(TelegramMediaWebFile.init),
receiptMessageId: nil,
currency: currency,
totalAmount: totalAmount,
startParam: "",
extendedMedia: nil,
flags: [],
version: TelegramMediaInvoice.lastVersion
)
let botPaymentId = PeerId.init(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId))
return BotPaymentReceipt(invoice: parsedInvoice, info: nil, shippingOption: nil, credentialsTitle: "", invoiceMedia: invoiceMedia, tipAmount: nil, botPaymentId: botPaymentId)
}
}
|> castError(RequestBotPaymentReceiptError.self)

View File

@ -183,7 +183,7 @@ private final class StarsContextImpl {
func add(balance: Int64) {
if var state = self._state {
var transactions = state.transactions
transactions.insert(.init(id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore), at: 0)
transactions.insert(.init(id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil), at: 0)
state.balance = state.balance + balance
self._state = state
@ -216,7 +216,7 @@ private final class StarsContextImpl {
private extension StarsContext.State.Transaction {
init?(apiTransaction: Api.StarsTransaction, transaction: Transaction) {
switch apiTransaction {
case let .starsTransaction(id, stars, date, transactionPeer):
case let .starsTransaction(_, id, stars, date, transactionPeer, title, description, photo):
let parsedPeer: StarsContext.State.Transaction.Peer
switch transactionPeer {
case .starsTransactionPeerAppStore:
@ -235,7 +235,7 @@ private extension StarsContext.State.Transaction {
}
parsedPeer = .peer(EnginePeer(peer))
}
self.init(id: id, count: stars, date: date, peer: parsedPeer)
self.init(id: id, count: stars, date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init))
}
}
}
@ -256,12 +256,26 @@ public final class StarsContext {
public let count: Int64
public let date: Int32
public let peer: Peer
public let title: String?
public let description: String?
public let photo: TelegramMediaWebFile?
init(id: String, count: Int64, date: Int32, peer: Peer) {
init(
id: String,
count: Int64,
date: Int32,
peer: Peer,
title: String?,
description: String?,
photo: TelegramMediaWebFile?
) {
self.id = id
self.count = count
self.date = date
self.peer = peer
self.title = title
self.description = description
self.photo = photo
}
}

View File

@ -175,7 +175,7 @@ public final class ChatBotInfoItemNode: ListViewItemNode {
break
case .ignore:
return .fail
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
return .waitForSingleTap
}
}

View File

@ -172,6 +172,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
let incoming = message.effectivelyIncoming(context.account.peerId)
let graphics = PresentationResourcesChat.additionalGraphics(theme.theme, wallpaper: theme.wallpaper, bubbleCorners: bubbleCorners)
var isStarsPayment = false
let iconImage: UIImage?
switch button.action {
case .text:
@ -191,7 +192,8 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
case .switchInline:
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingShareIconImage : graphics.chatBubbleActionButtonOutgoingShareIconImage
case .payment:
if button.title.contains("Pay XTR") {
if button.title.contains("⭐️") {
isStarsPayment = true
iconImage = nil
} else {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingPaymentIconImage : graphics.chatBubbleActionButtonOutgoingPaymentIconImage
@ -222,10 +224,10 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
let titleColor = bubbleVariableColor(variableColor: messageTheme.actionButtonsTextColor, wallpaper: theme.wallpaper)
let attributedTitle: NSAttributedString
if title.contains("Pay XTR") {
let stars = title.replacingOccurrences(of: "Pay XTR", with: "")
let buttonAttributedString = NSMutableAttributedString(string: "Pay > \(stars)", font: titleFont, textColor: titleColor, paragraphAlignment: .center)
if let range = buttonAttributedString.string.range(of: ">"), let starImage = UIImage(bundleImageName: "Item List/PremiumIcon") {
if isStarsPayment {
let updatedTitle = title.replacingOccurrences(of: "⭐️", with: " # ")
let buttonAttributedString = NSMutableAttributedString(string: updatedTitle, font: titleFont, textColor: titleColor, paragraphAlignment: .center)
if let range = buttonAttributedString.string.range(of: "#"), let starImage = UIImage(bundleImageName: "Item List/PremiumIcon") {
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.foregroundColor, value: titleColor, range: NSRange(range, in: buttonAttributedString.string))
buttonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: buttonAttributedString.string))

View File

@ -159,6 +159,7 @@ public struct ChatMessageBubbleContentTapAction {
case copy(String)
case largeEmoji(String, String?, TelegramMediaFile)
case customEmoji(TelegramMediaFile)
case custom(() -> Void)
}
public var content: Content

View File

@ -1178,7 +1178,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
break
case .ignore:
return .fail
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
return .waitForSingleTap
}
}
@ -4623,6 +4623,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
return .action(InternalBubbleTapAction.Action {
})
}
case let .custom(action):
return .action(InternalBubbleTapAction.Action({
action()
}, contextMenuOnLongPress: !tapAction.hasLongTapAction))
case let .url(url):
if case .longTap = gesture, !tapAction.hasLongTapAction, let item = self.item {
let tapMessage = item.content.firstMessage
@ -4872,6 +4876,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
break
case .customEmoji:
break
case .custom:
break
}
}
if let tapMessage = tapMessage {

View File

@ -24,6 +24,7 @@ swift_library(
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
"//submodules/TextSelectionNode",
],
visibility = [
"//visibility:public",

View File

@ -12,6 +12,7 @@ import ChatMessageDateAndStatusNode
import ChatMessageBubbleContentNode
import ChatMessageItemCommon
import MessageInlineBlockBackgroundView
import TextSelectionNode
import Geocoding
import UrlEscaping
@ -44,6 +45,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
private let textNode: TextNode
private let additionalTextNode: TextNode
private var linkHighlightingNode: LinkHighlightingNode?
private var textSelectionNode: TextSelectionNode?
private let lineNode: ASDisplayNode
@ -51,7 +53,6 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
private var maskOverlayView: UIView?
private var expandIcon: ASImageNode
private var expandButton: HighlightTrackingButtonNode?
private let statusNode: ChatMessageDateAndStatusNode
@ -136,10 +137,58 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
}
public override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
if let expandButton = self.expandButton, expandButton.frame.contains(point) {
return ChatMessageBubbleContentTapAction(content: .ignore)
public override func willUpdateIsExtractedToContextPreview(_ value: Bool) {
if !value {
if let textSelectionNode = self.textSelectionNode {
self.textSelectionNode = nil
textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
textSelectionNode?.highlightAreaNode.removeFromSupernode()
textSelectionNode?.removeFromSupernode()
})
}
}
}
public override func updateIsExtractedToContextPreview(_ value: Bool) {
if value {
if self.textSelectionNode == nil, let item = self.item, let rootNode = item.controllerInteraction.chatControllerNode() {
let selectionColor: UIColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionColor
let knobColor: UIColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionKnobColor
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: selectionColor, knob: knobColor, isDark: item.presentationData.theme.theme.overallDarkAppearance), strings: item.presentationData.strings, textNode: self.textNode, updateIsActive: { [weak self] value in
self?.updateIsTextSelectionActive?(value)
}, present: { [weak self] c, a in
self?.item?.controllerInteraction.presentGlobalOverlayController(c, a)
}, rootNode: { [weak rootNode] in
return rootNode
}, performAction: { [weak self] text, action in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.controllerInteraction.performTextSelectionAction(item.message, true, text, action)
})
textSelectionNode.enableQuote = false
self.textSelectionNode = textSelectionNode
self.addSubnode(textSelectionNode)
self.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textClippingNode)
textSelectionNode.frame = self.textClippingNode.view.convert(self.textNode.frame, to: self.view)
textSelectionNode.highlightAreaNode.frame = textSelectionNode.frame
}
} else {
if let textSelectionNode = self.textSelectionNode {
self.textSelectionNode = nil
self.updateIsTextSelectionActive?(false)
textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
textSelectionNode?.highlightAreaNode.removeFromSupernode()
textSelectionNode?.removeFromSupernode()
})
}
}
}
public override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
if let titleBadgeButton = self.titleBadgeButton, titleBadgeButton.frame.contains(point) {
return ChatMessageBubbleContentTapAction(content: .ignore)
}
@ -162,6 +211,11 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag))
}
}
if let backgroundView = self.backgroundView, backgroundView.frame.contains(point), case .tap = gesture {
return ChatMessageBubbleContentTapAction(content: .custom({ [weak self] in
self?.expandPressed()
}), hasLongTapAction: false)
}
return ChatMessageBubbleContentTapAction(content: .none)
}
@ -585,35 +639,21 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
}
strongSelf.expandIcon.bounds = CGRect(origin: .zero, size: expandIconFrame.size)
}
let expandButtonFrame = expandIconFrame.insetBy(dx: -8.0, dy: -8.0)
let expandButton: HighlightTrackingButtonNode
if let current = strongSelf.expandButton {
expandButton = current
} else {
expandButton = HighlightTrackingButtonNode()
expandButton.addTarget(self, action: #selector(strongSelf.expandPressed), forControlEvents: .touchUpInside)
expandButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.expandIcon.layer.removeAnimation(forKey: "opacity")
strongSelf.expandIcon.alpha = 0.4
} else {
strongSelf.expandIcon.alpha = 1.0
strongSelf.expandIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
strongSelf.expandButton = expandButton
strongSelf.addSubnode(expandButton)
}
expandButton.frame = expandButtonFrame
} else {
strongSelf.expandIcon.isHidden = true
strongSelf.textClippingNode.view.mask = nil
}
if let textSelectionNode = strongSelf.textSelectionNode {
let shouldUpdateLayout = textSelectionNode.frame.size != textFrame.size
textSelectionNode.frame = strongSelf.textClippingNode.view.convert(strongSelf.textNode.frame, to: strongSelf.view)
textSelectionNode.highlightAreaNode.frame = textSelectionNode.frame
if shouldUpdateLayout {
textSelectionNode.updateLayout()
}
}
if let statusSizeAndApply = statusSizeAndApply {
strongSelf.statusNode.reactionSelected = { [weak strongSelf] _, value, sourceView in
guard let strongSelf, let item = strongSelf.item else {

View File

@ -494,7 +494,12 @@ public class ChatMessageThreadInfoNode: ASDisplayNode {
node.contentNode.addSubnode(avatarNode)
}
avatarNode.frame = CGRect(origin: CGPoint(x: -1.0, y: -3.0), size: CGSize(width: 26.0, height: 26.0))
avatarNode.setPeer(context: arguments.context, theme: arguments.presentationData.theme.theme, peer: peer)
var overrideImage: AvatarNodeImageOverride?
if peer.id.isReplies {
overrideImage = .repliesIcon
}
avatarNode.setPeer(context: arguments.context, theme: arguments.presentationData.theme.theme, peer: peer, overrideImage: overrideImage)
} else {
let titleTopicIconView: ComponentHostView<Empty>
if let current = node.titleTopicIconView {

View File

@ -222,7 +222,7 @@ private final class FactCheckAlertContentNode: AlertContentNode {
characterLimit: 1024,
emptyLineHandling: .oneConsecutive,
formatMenuAvailability: .available([.bold, .italic, .link]),
returnKeyType: .done,
returnKeyType: .default,
lockedFormatAction: {
},
present: { [weak self] c in

View File

@ -33,7 +33,11 @@ private func generateShineTexture() -> UIImage {
private func generateDiffuseTexture(colors: [UIColor]) -> UIImage {
return generateImage(CGSize(width: 256, height: 256), rotatedContext: { size, context in
let colorsArray: [CGColor] = colors.map { $0.cgColor }
var locations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0]
var locations: [CGFloat] = []
for i in 0 ..< colors.count {
let t = CGFloat(i) / CGFloat(colors.count - 1)
locations.append(t)
}
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
@ -281,7 +285,7 @@ public final class PremiumStarComponent: Component {
private var didSetup = false
private func setup() {
guard !self.didSetup, let scene = loadCompressedScene(name: "star", version: sceneVersion) else {
guard !self.didSetup, let scene = loadCompressedScene(name: "star2", version: sceneVersion) else {
return
}
@ -289,23 +293,23 @@ public final class PremiumStarComponent: Component {
self.sceneView.scene = scene
self.sceneView.delegate = self
if let node = scene.rootNode.childNode(withName: "star", recursively: false), let colors = self.component?.colors, let color = colors.first {
if let node = scene.rootNode.childNode(withName: "star", recursively: false), let colors = self.component?.colors, let _ = colors.first {
node.geometry?.materials.first?.diffuse.contents = generateDiffuseTexture(colors: colors)
let names: [String] = [
"particles_left",
"particles_right",
"particles_left_bottom",
"particles_right_bottom",
"particles_center"
]
for name in names {
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
particleSystem.particleColor = color
particleSystem.particleColorVariation = SCNVector4Make(0, 0, 0, 0)
}
}
// let names: [String] = [
// "particles_left",
// "particles_right",
// "particles_left_bottom",
// "particles_right_bottom",
// "particles_center"
// ]
//
// for name in names {
// if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
// particleSystem.particleColor = color
// particleSystem.particleColorVariation = SCNVector4Make(0, 0, 0, 0)
// }
// }
}
if self.animateFrom != nil {
@ -450,9 +454,9 @@ public final class PremiumStarComponent: Component {
return
}
if #available(iOS 17.0, *), let material = node.geometry?.materials.first {
material.metalness.intensity = 0.2
}
// if let material = node.geometry?.materials.first {
// material.metalness.intensity = 0.4
// }
let animation = CABasicAnimation(keyPath: "contentsTransform")
animation.fillMode = .forwards

View File

@ -696,10 +696,10 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
isVisible: starIsVisible,
hasIdleAnimations: state.hasIdleAnimations,
colors: [
UIColor(rgb: 0xea8904),
UIColor(rgb: 0xe57d02),
UIColor(rgb: 0xf09903),
UIColor(rgb: 0xfec209),
UIColor(rgb: 0xfed31a)
UIColor(rgb: 0xf9b004),
UIColor(rgb: 0xfdd219)
]
),
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),

View File

@ -371,10 +371,10 @@ final class StarsTransactionsScreenComponent: Component {
isVisible: true,
hasIdleAnimations: true,
colors: [
UIColor(rgb: 0xea8904),
UIColor(rgb: 0xe57d02),
UIColor(rgb: 0xf09903),
UIColor(rgb: 0xfec209),
UIColor(rgb: 0xfed31a)
UIColor(rgb: 0xf9b004),
UIColor(rgb: 0xfdd219)
]
)),
environment: {},

View File

@ -216,7 +216,8 @@ private final class SheetContent: CombinedComponent {
let constrainedTitleWidth = context.availableSize.width - 16.0 * 2.0
contentSize.height += 130.0
contentSize.height += 126.0
let title = title.update(
component: Text(text: strings.Stars_Transfer_Title, font: Font.bold(24.0), color: theme.list.itemPrimaryTextColor),
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
@ -258,13 +259,14 @@ private final class SheetContent: CombinedComponent {
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0))
)
contentSize.height += text.size.height
contentSize.height += 24.0
contentSize.height += 28.0
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== theme {
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: UIColor(rgb: 0xf09903))!, theme)
}
let balanceAttributedString = parseMarkdownIntoAttributedString("\(strings.Stars_Transfer_Balance)\n # **\(state.balance ?? 0)**", attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
let balanceAttributedString = NSMutableAttributedString(string: strings.Stars_Transfer_Balance, font: Font.regular(14.0), textColor: textColor)
balanceAttributedString.append(NSMutableAttributedString(string: "\n # \(state.balance ?? 0)", font: Font.semibold(16.0), textColor: textColor))
if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = state.cachedChevronImage?.0 {
balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string))
@ -274,13 +276,14 @@ private final class SheetContent: CombinedComponent {
component: MultilineTextComponent(
text: .plain(balanceAttributedString),
horizontalAlignment: .left,
maximumNumberOfLines: 0
maximumNumberOfLines: 0,
lineSpacing: 0.25
),
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
transition: .immediate
)
context.add(balanceText
.position(CGPoint(x: 16.0 + balanceText.size.width / 2.0, y: 29.0))
.position(CGPoint(x: 16.0 + balanceText.size.width / 2.0, y: 31.0))
)
if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme {
@ -315,14 +318,17 @@ private final class SheetContent: CombinedComponent {
isEnabled: true,
displaysProgress: state.inProgress,
action: { [weak state, weak controller] in
state?.buy(requestTopUp: { [weak controller] _ in
state?.buy(requestTopUp: { [weak controller] completion in
let purchaseController = accountContext.sharedContext.makeStarsPurchaseScreen(
context: accountContext,
starsContext: starsContext,
options: state?.options ?? [],
peerId: state?.peer?.id,
requiredStars: invoice.totalAmount,
completion: { _ in }
completion: { [weak starsContext] stars in
starsContext?.add(balance: stars)
completion()
}
)
controller?.push(purchaseController)
}, completion: { [weak controller] in