Various improvements

This commit is contained in:
Ilya Laktyushin 2024-08-09 17:43:47 +02:00
parent d17391ad9b
commit 2bf11a6279
31 changed files with 340 additions and 126 deletions

View File

@ -12344,6 +12344,7 @@ Sorry for the inconvenience.";
"Stars.Transfer.Purchased.Stars_any" = "%@ Stars";
"Stars.Transfer.UnlockedText" = "You unlocked media for **%1$@**.";
"Stars.Transfer.UnlockInfo" = "Do you want to unlock %1$@ in **%2$@** for **%3$@**?";
"Stars.Transfer.UnlockBotInfo" = "Do you want to unlock %1$@ from **%2$@** for **%3$@**?";
"Stars.Transfer.Balance" = "Balance";
@ -12695,6 +12696,9 @@ Sorry for the inconvenience.";
"Stars.Transaction.Subscription.Cancelled" = "You have cancelled your subscription.";
"Stars.Transaction.Subscription.Renew" = "Renew Subscription";
"Stars.Transaction.Subscription.Cancel" = "Cancel Subscription";
"Stars.Transaction.Subscription.JoinChannel" = "Join Channel";
"Stars.Transaction.Subscription.JoinAgainChannel" = "Join Channel";
"Stars.Transaction.Subscription.LeftChannel" = "You left channel but you can still get back until %@";
"Stars.Transaction.Subscription.PerMonth" = "%@ / month";
"Stars.Transaction.Subscription.PerMonthUsd" = "appx. %@ per month";
"Stars.Transaction.Subscription.Subscription" = "Subscription";
@ -12721,3 +12725,14 @@ Sorry for the inconvenience.";
"Stars.Intro.Transaction.SubscriptionFee.Title" = "Monthly Subscription Fee";
"Stars.Intro.Transaction.Reaction.Title" = "Star Reaction";
"Stars.Purchase.GenericPurchasePurpose" = "Buy Stars to unlock content and services on Telegram.";
"Stars.Purchase.PurchasePurpose.subs" = "Buy Stars to keep all your subscriptions.";
"Stars.Transfer.Subscribe.Channel.Title" = "Subscribe";
"Stars.Transfer.SubscribeInfo" = "Do you want to subscribe to **%1$@** for **%2$@** per month?";
"Stars.Transfer.Subscribe" = "Subscribe";
"Stars.Transfer.Subscribe.Successful.Title" = "Subscription successful!";
"Stars.Transfer.Subscribe.Successful.Text" = "%1$@ transferred to %2$@.";
"Gallery.Ad" = "Ad";

View File

@ -305,7 +305,7 @@ public enum ResolvedUrl {
case startAttach(peerId: PeerId, payload: String?, choose: ResolvedBotChoosePeerTypes?)
case invoice(slug: String, invoice: TelegramMediaInvoice?)
case premiumOffer(reference: String?)
case starsTopup(amount: Int64?)
case starsTopup(amount: Int64, purpose: String?)
case chatFolder(slug: String)
case story(peerId: PeerId, id: Int32)
case boost(peerId: PeerId?, status: ChannelBoostStatus?, myBoostStatus: MyBoostStatus?)
@ -917,7 +917,7 @@ public protocol SharedAccountContext: AnyObject {
func makeProxySettingsController(context: AccountContext) -> ViewController
func makeLocalizationListController(context: AccountContext) -> ViewController
func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) -> ViewController
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
func makeBioPrivacyController(context: AccountContext, settings: Promise<AccountPrivacySettings?>, present: @escaping (ViewController) -> Void)
func makeBirthdayPrivacyController(context: AccountContext, settings: Promise<AccountPrivacySettings?>, openedFromBirthdayScreen: Bool, present: @escaping (ViewController) -> Void)

View File

@ -123,7 +123,8 @@ public enum BoostSubject: Equatable {
}
public enum StarsPurchasePurpose: Equatable {
case generic(requiredStars: Int64?)
case generic
case topUp(requiredStars: Int64, purpose: String?)
case transfer(peerId: EnginePeer.Id, requiredStars: Int64)
case subscription(peerId: EnginePeer.Id, requiredStars: Int64, renew: Bool)
case gift(peerId: EnginePeer.Id)

View File

@ -256,7 +256,13 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
}
}
if message.containsSecretMedia {
if let adAttribute = message.adAttribute, adAttribute.hasContentMedia {
let gallery = GalleryController(context: context, source: .standaloneMessage(message, mediaIndex), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: nil, playbackRate: 1.0, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
gallery.temporaryDoNotWaitForReady = autoplayingVideo
return .gallery(.single(gallery))
} else if message.containsSecretMedia {
let gallery = SecretMediaPreviewController(context: context, messageId: message.id)
return .secretGallery(gallery)
} else {

View File

@ -770,10 +770,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
}
func setup(origin: GalleryItemOriginData?, caption: NSAttributedString) {
let titleText = origin?.title
func setup(origin: GalleryItemOriginData?, caption: NSAttributedString, isAd: Bool = false) {
var titleText = origin?.title
let dateText = origin?.timestamp.flatMap { humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: $0).string }
let caption = caption.mutableCopy() as! NSMutableAttributedString
if isAd {
if let titleText, !titleText.isEmpty {
caption.insert(NSAttributedString(string: titleText + "\n", font: Font.semibold(17.0), textColor: .white), at: 0)
}
titleText = nil
}
if self.currentMessageText != caption || self.currentAuthorNameText != titleText || self.currentDateText != dateText {
self.currentMessageText = caption
self.currentAuthorNameText = titleText
@ -820,9 +828,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
if Namespaces.Message.allNonRegular.contains(message.id.namespace) || message.timestamp == 0 {
displayInfo = false
}
if let _ = message.adAttribute {
displayInfo = false
}
var canDelete: Bool
var canShare = !message.containsSecretMedia && !Namespaces.Message.allNonRegular.contains(message.id.namespace)
var canShare = !message.containsSecretMedia && !Namespaces.Message.allNonRegular.contains(message.id.namespace) && message.adAttribute == nil
var canFullscreen = false
@ -922,13 +933,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
var dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: message.timestamp).string
if !displayInfo {
authorNameText = ""
dateText = ""
canEdit = false
}
var messageText = NSAttributedString(string: "")
var messageText = NSMutableAttributedString(string: "")
var hasCaption = false
for media in message.media {
if media is TelegramMediaPaidContent {
@ -991,7 +997,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
codeHighlightState.disposable.dispose()
}
messageText = galleryCaptionStringWithAppliedEntities(context: self.context, text: text, entities: entities, message: message, cachedMessageSyntaxHighlight: cachedMessageSyntaxHighlight)
messageText = galleryCaptionStringWithAppliedEntities(context: self.context, text: text, entities: entities, message: message, cachedMessageSyntaxHighlight: cachedMessageSyntaxHighlight).mutableCopy() as! NSMutableAttributedString
messageText.insert(NSAttributedString(string: (authorNameText ?? "") + "\n", font: Font.semibold(17.0), textColor: .white), at: 0)
}
if !displayInfo {
authorNameText = ""
dateText = ""
canEdit = false
}
if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canFullscreen != !self.fullscreenButton.isHidden || canShare != !self.actionButton.isHidden || canEdit != !self.editButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText {

View File

@ -168,7 +168,9 @@ class ChatImageGalleryItem: GalleryItem {
}
}
if let location = self.location {
if let _ = message.adAttribute {
node._title.set(.single(self.presentationData.strings.Gallery_Ad))
} else if let location = self.location {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").string))
}

View File

@ -1528,15 +1528,17 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.playbackRatePromise.set(self.playbackRate ?? 1.0)
var isAd = false
if let contentInfo = item.contentInfo {
switch contentInfo {
case let .message(message, _):
isAd = message.adAttribute != nil
self.footerContentNode.setMessage(message, displayInfo: !item.displayInfoOnTop, peerIsCopyProtected: item.peerIsCopyProtected)
case let .webPage(webPage, media, _):
self.footerContentNode.setWebPage(webPage, media: media)
}
}
self.footerContentNode.setup(origin: item.originData, caption: item.caption)
self.footerContentNode.setup(origin: item.originData, caption: item.caption, isAd: isAd)
}
override func controlsVisibilityUpdated(isVisible: Bool) {

View File

@ -857,18 +857,20 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
private struct StarsSubscriptionConfiguration {
static var defaultValue: StarsSubscriptionConfiguration {
return StarsSubscriptionConfiguration(maxFee: 2500)
return StarsSubscriptionConfiguration(maxFee: 2500, usdSellRate: 2000)
}
let maxFee: Int64?
let usdSellRate: Int64?
fileprivate init(maxFee: Int64?) {
fileprivate init(maxFee: Int64?, usdSellRate: Int64?) {
self.maxFee = maxFee
self.usdSellRate = usdSellRate
}
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double {
return StarsSubscriptionConfiguration(maxFee: Int64(value))
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double, let usdRate = data["stars_usd_sell_rate_x1000"] as? Double {
return StarsSubscriptionConfiguration(maxFee: Int64(value), usdSellRate: Int64(usdRate))
} else {
return .defaultValue
}

View File

@ -182,6 +182,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
private var absoluteLocation: (CGRect, CGSize)?
private var currentColor: ItemBackgroundColor?
private var currentIsPaid: Bool?
private var layoutParams: (ItemListInviteLinkItem, ListViewItemLayoutParams, ItemListNeighbors, Bool, Bool)?
public var tag: ItemListItemTag?
@ -548,8 +549,12 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
if let _ = item.invite?.pricing {
}
let isPaid = item.invite?.pricing != nil
if updatedTheme != nil || strongSelf.currentIsPaid != isPaid {
strongSelf.currentIsPaid = isPaid
if isPaid {
strongSelf.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/SubscriptionLink"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor)
} else {
strongSelf.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/InviteLink"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor)

View File

@ -1358,6 +1358,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
let _ = statusApply()
if case let .account(context) = item.context {
let _ = labelApply(TextNodeWithEntities.Arguments(context: context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, attemptSynchronous: false))
} else {
let _ = labelApply(nil)
}
strongSelf.labelNode.textNode.isHidden = labelAttributedString == nil

View File

@ -709,7 +709,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
}
if case .legacyGroup = peer {
} else {
pushControllerImpl?(context.sharedContext.makeChatRecentActionsController(context: context, peer: peer._asPeer(), adminPeerId: nil))
pushControllerImpl?(context.sharedContext.makeChatRecentActionsController(context: context, peer: peer._asPeer(), adminPeerId: nil, starsState: nil))
}
})
})

View File

@ -890,7 +890,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
let controller = context.sharedContext.makeChatRecentActionsController(context: context, peer: peer, adminPeerId: participantPeerId)
let controller = context.sharedContext.makeChatRecentActionsController(context: context, peer: peer, adminPeerId: participantPeerId, starsState: nil)
navigationController.pushViewController(controller)
})
}

View File

@ -881,7 +881,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) }
dict[-425595208] = { return Api.SmsJob.parse_smsJob($0) }
dict[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1301522832] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
dict[1577421297] = { return Api.StarsGiftOption.parse_starsGiftOption($0) }
dict[2033461574] = { return Api.StarsRevenueStatus.parse_starsRevenueStatus($0) }

View File

@ -441,14 +441,14 @@ public extension Api {
}
}
public extension Api {
enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, url: String, title: String, message: String, entities: [Api.MessageEntity]?, photo: Api.Photo?, color: Api.PeerColor?, buttonText: String, sponsorInfo: String?, additionalInfo: String?)
indirect enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, url: String, title: String, message: String, entities: [Api.MessageEntity]?, photo: Api.Photo?, media: Api.MessageMedia?, color: Api.PeerColor?, buttonText: String, sponsorInfo: String?, additionalInfo: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let color, let buttonText, let sponsorInfo, let additionalInfo):
case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo):
if boxed {
buffer.appendInt32(-1108478618)
buffer.appendInt32(1301522832)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeBytes(randomId, buffer: buffer, boxed: false)
@ -461,6 +461,7 @@ public extension Api {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 6) != 0 {photo!.serialize(buffer, true)}
if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)}
if Int(flags) & Int(1 << 13) != 0 {color!.serialize(buffer, true)}
serializeString(buttonText, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)}
@ -471,8 +472,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let color, let buttonText, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("url", url as Any), ("title", title as Any), ("message", message as Any), ("entities", entities as Any), ("photo", photo as Any), ("color", color as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("url", url as Any), ("title", title as Any), ("message", message as Any), ("entities", entities as Any), ("photo", photo as Any), ("media", media as Any), ("color", color as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
}
}
@ -495,16 +496,20 @@ public extension Api {
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _8: Api.PeerColor?
if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.PeerColor
var _8: Api.MessageMedia?
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.MessageMedia
} }
var _9: Api.PeerColor?
if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.PeerColor
} }
var _9: String?
_9 = parseString(reader)
var _10: String?
if Int(_1!) & Int(1 << 7) != 0 {_10 = parseString(reader) }
_10 = parseString(reader)
var _11: String?
if Int(_1!) & Int(1 << 8) != 0 {_11 = parseString(reader) }
if Int(_1!) & Int(1 << 7) != 0 {_11 = parseString(reader) }
var _12: String?
if Int(_1!) & Int(1 << 8) != 0 {_12 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -512,12 +517,13 @@ public extension Api {
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
let _c9 = _9 != nil
let _c10 = (Int(_1!) & Int(1 << 7) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, color: _8, buttonText: _9!, sponsorInfo: _10, additionalInfo: _11)
let _c8 = (Int(_1!) & Int(1 << 14) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 13) == 0) || _9 != nil
let _c10 = _10 != nil
let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, media: _8, color: _9, buttonText: _10!, sponsorInfo: _11, additionalInfo: _12)
}
else {
return nil

View File

@ -14,8 +14,9 @@ public final class AdMessageAttribute: MessageAttribute {
public let sponsorInfo: String?
public let additionalInfo: String?
public let canReport: Bool
public let hasContentMedia: Bool
public init(opaqueId: Data, messageType: MessageType, url: String, buttonText: String, sponsorInfo: String?, additionalInfo: String?, canReport: Bool) {
public init(opaqueId: Data, messageType: MessageType, url: String, buttonText: String, sponsorInfo: String?, additionalInfo: String?, canReport: Bool, hasContentMedia: Bool) {
self.opaqueId = opaqueId
self.messageType = messageType
self.url = url
@ -23,6 +24,7 @@ public final class AdMessageAttribute: MessageAttribute {
self.sponsorInfo = sponsorInfo
self.additionalInfo = additionalInfo
self.canReport = canReport
self.hasContentMedia = hasContentMedia
}
public init(decoder: PostboxDecoder) {

View File

@ -12,6 +12,7 @@ private class AdMessagesHistoryContextImpl {
case text
case textEntities
case media
case contentMedia
case color
case backgroundEmojiId
case url
@ -32,6 +33,7 @@ private class AdMessagesHistoryContextImpl {
public let text: String
public let textEntities: [MessageTextEntity]
public let media: [Media]
public let contentMedia: [Media]
public let color: PeerNameColor?
public let backgroundEmojiId: Int64?
public let url: String
@ -47,6 +49,7 @@ private class AdMessagesHistoryContextImpl {
text: String,
textEntities: [MessageTextEntity],
media: [Media],
contentMedia: [Media],
color: PeerNameColor?,
backgroundEmojiId: Int64?,
url: String,
@ -61,6 +64,7 @@ private class AdMessagesHistoryContextImpl {
self.text = text
self.textEntities = textEntities
self.media = media
self.contentMedia = contentMedia
self.color = color
self.backgroundEmojiId = backgroundEmojiId
self.url = url
@ -89,6 +93,12 @@ private class AdMessagesHistoryContextImpl {
self.media = mediaData.compactMap { data -> Media? in
return PostboxDecoder(buffer: MemoryBuffer(data: data)).decodeRootObject() as? Media
}
let contentMediaData = try container.decode([Data].self, forKey: .contentMedia)
self.contentMedia = contentMediaData.compactMap { data -> Media? in
return PostboxDecoder(buffer: MemoryBuffer(data: data)).decodeRootObject() as? Media
}
self.color = try container.decodeIfPresent(Int32.self, forKey: .color).flatMap { PeerNameColor(rawValue: $0) }
self.backgroundEmojiId = try container.decodeIfPresent(Int64.self, forKey: .backgroundEmojiId)
@ -116,6 +126,13 @@ private class AdMessagesHistoryContextImpl {
return encoder.makeData()
}
try container.encode(mediaData, forKey: .media)
let contentMediaData = self.contentMedia.map { media -> Data in
let encoder = PostboxEncoder()
encoder.encodeRootObject(media)
return encoder.makeData()
}
try container.encode(contentMediaData, forKey: .contentMedia)
try container.encodeIfPresent(self.color?.rawValue, forKey: .color)
try container.encodeIfPresent(self.backgroundEmojiId, forKey: .backgroundEmojiId)
@ -153,6 +170,14 @@ private class AdMessagesHistoryContextImpl {
return false
}
}
if lhs.contentMedia.count != rhs.contentMedia.count {
return false
}
for i in 0 ..< lhs.contentMedia.count {
if !lhs.contentMedia[i].isEqual(to: rhs.contentMedia[i]) {
return false
}
}
if lhs.url != rhs.url {
return false
}
@ -181,7 +206,7 @@ private class AdMessagesHistoryContextImpl {
case .recommended:
mappedMessageType = .recommended
}
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, url: self.url, buttonText: self.buttonText, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo, canReport: self.canReport))
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, url: self.url, buttonText: self.buttonText, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo, canReport: self.canReport, hasContentMedia: !self.contentMedia.isEmpty))
if !self.textEntities.isEmpty {
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
attributes.append(attribute)
@ -241,7 +266,7 @@ private class AdMessagesHistoryContextImpl {
author: author,
text: self.text,
attributes: attributes,
media: self.media,
media: !self.contentMedia.isEmpty ? self.contentMedia : self.media,
peers: messagePeers,
associatedMessages: SimpleDictionary<MessageId, Message>(),
associatedMessageIds: [],
@ -422,7 +447,7 @@ private class AdMessagesHistoryContextImpl {
for message in messages {
switch message {
case let .sponsoredMessage(flags, randomId, url, title, message, entities, photo, color, buttonText, sponsorInfo, additionalInfo):
case let .sponsoredMessage(flags, randomId, url, title, message, entities, photo, media, color, buttonText, sponsorInfo, additionalInfo):
var parsedEntities: [MessageTextEntity] = []
if let entities = entities {
parsedEntities = messageTextEntitiesFromApiEntities(entities)
@ -442,6 +467,8 @@ private class AdMessagesHistoryContextImpl {
}
let photo = photo.flatMap { telegramMediaImageFromApiPhoto($0) }
let (contentMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
parsedMessages.append(CachedMessage(
opaqueId: randomId.makeData(),
messageType: isRecommended ? .recommended : .sponsored,
@ -449,6 +476,7 @@ private class AdMessagesHistoryContextImpl {
text: message,
textEntities: parsedEntities,
media: photo.flatMap { [$0] } ?? [],
contentMedia: contentMedia.flatMap { [$0] } ?? [],
color: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) },
backgroundEmojiId: backgroundEmojiId,
url: url,

View File

@ -686,7 +686,7 @@ public final class StarsContext {
return peerId!
}
var currentState: StarsContext.State? {
public var currentState: StarsContext.State? {
var state: StarsContext.State?
self.impl.syncWith { impl in
state = impl._state

View File

@ -637,6 +637,14 @@ public struct PeerInvitationImportersState: Equatable {
public var about: String?
public var approvedBy: PeerId?
public var joinedViaFolderLink: Bool
public init(peer: RenderedPeer, date: Int32, about: String? = nil, approvedBy: PeerId? = nil, joinedViaFolderLink: Bool) {
self.peer = peer
self.date = date
self.about = about
self.approvedBy = approvedBy
self.joinedViaFolderLink = joinedViaFolderLink
}
}
public var importers: [Importer]
public var isLoadingMore: Bool

View File

@ -318,6 +318,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
flags.remove(.preferMediaInline)
mediaAndFlags = (mediaAndFlagsValue.0, flags)
}
if let adAttribute = message.adAttribute, adAttribute.hasContentMedia {
var flags = mediaAndFlagsValue.1
flags.remove(.preferMediaInline)
flags.insert(.preferMediaBeforeText)
mediaAndFlags = (mediaAndFlagsValue.0, flags)
}
}
var contentMediaAspectFilled = false

View File

@ -82,7 +82,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
}
}
}
let openChatMessageMode: ChatControllerInteractionOpenMessageMode
var openChatMessageMode: ChatControllerInteractionOpenMessageMode
switch mode {
case .default:
openChatMessageMode = .default
@ -91,6 +91,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
case .automaticPlayback:
openChatMessageMode = .automaticPlayback
}
if let adAttribute = item.message.adAttribute, adAttribute.hasContentMedia {
openChatMessageMode = .automaticPlayback
}
if !item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: openChatMessageMode)) {
if let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
var isConcealed = true

View File

@ -22,6 +22,8 @@ public final class ChatRecentActionsController: TelegramBaseController {
private let context: AccountContext
private let peer: Peer
private let initialAdminPeerId: PeerId?
let starsState: StarsRevenueStats?
private var presentationData: PresentationData
private var presentationDataPromise = Promise<PresentationData>()
override public var updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) {
@ -37,10 +39,11 @@ public final class ChatRecentActionsController: TelegramBaseController {
private var adminsDisposable: Disposable?
public init(context: AccountContext, peer: Peer, adminPeerId: PeerId?) {
public init(context: AccountContext, peer: Peer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) {
self.context = context
self.peer = peer
self.initialAdminPeerId = adminPeerId
self.starsState = starsState
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -210,10 +210,16 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
])])
strongSelf.presentController(actionSheet, .window(.root), nil)
} else {
let controller = inviteLinkEditController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peerId: peer.id, invite: invite, completion: { [weak self] _ in
self?.eventLogContext.reload()
})
controller.navigationPresentation = .modal
let controller = InviteLinkViewController(
context: strongSelf.context,
updatedPresentationData: strongSelf.controller?.updatedPresentationData,
peerId: peer.id,
invite: invite,
invitationsContext: nil,
revokedInvitationsContext: nil,
importersContext: nil,
starsState: strongSelf.controller?.starsState
)
strongSelf.pushController(controller)
}
return true

View File

@ -8584,7 +8584,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
guard let peer = self.data?.peer else {
return
}
let controller = self.context.sharedContext.makeChatRecentActionsController(context: self.context, peer: peer, adminPeerId: nil)
let controller = self.context.sharedContext.makeChatRecentActionsController(context: self.context, peer: peer, adminPeerId: nil, starsState: self.data?.starsRevenueStatsState)
self.controller?.push(controller)
}

View File

@ -208,9 +208,24 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
let textString: String
switch context.component.purpose {
case let .generic(requiredStars):
let _ = requiredStars
case .generic:
textString = strings.Stars_Purchase_GetStarsInfo
case let .topUp(_, purpose):
var text = strings.Stars_Purchase_GenericPurchasePurpose
if let purpose, !purpose.isEmpty {
switch purpose {
case "subs":
text = strings.Stars_Purchase_PurchasePurpose_subs
default:
let key = "Stars.Purchase.PurchasePurpose.\(purpose)"
if let string = strings.primaryComponent.dict[key] {
text = string
} else if let string = strings.secondaryComponent?.dict[key] {
text = string
}
}
}
textString = text
case .gift:
textString = strings.Stars_Purchase_GiftInfo(component.peers.first?.value.compactDisplayTitle ?? "").string
case .transfer:
@ -816,12 +831,10 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
let titleText: String
switch context.component.purpose {
case let .generic(requiredStars):
if let requiredStars {
titleText = strings.Stars_Purchase_StarsNeeded(Int32(requiredStars))
} else {
titleText = strings.Stars_Purchase_GetStars
}
case .generic:
titleText = strings.Stars_Purchase_GetStars
case let .topUp(requiredStars, _):
titleText = strings.Stars_Purchase_StarsNeeded(Int32(requiredStars))
case .gift:
titleText = strings.Stars_Purchase_GiftStars
case let .transfer(_, requiredStars), let .subscription(_, requiredStars, _), let .unlockMedia(requiredStars):
@ -1226,7 +1239,7 @@ private extension StarsPurchasePurpose {
var requiredStars: Int64? {
switch self {
case let .generic(requiredStars):
case let .topUp(requiredStars, _):
return requiredStars
case let .transfer(_, requiredStars):
return requiredStars

View File

@ -36,7 +36,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
let openAppExamples: () -> Void
let copyTransactionId: (String) -> Void
let updateSubscription: (StarsTransactionScreen.SubscriptionAction) -> Void
let updateSubscription: () -> Void
init(
context: AccountContext,
@ -47,7 +47,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
openAppExamples: @escaping () -> Void,
copyTransactionId: @escaping (String) -> Void,
updateSubscription: @escaping (StarsTransactionScreen.SubscriptionAction) -> Void
updateSubscription: @escaping () -> Void
) {
self.context = context
self.subject = subject
@ -199,6 +199,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var countOnTop = false
var transactionId: String?
let date: Int32
var additionalDate: Int32?
var via: String?
var messageId: EngineMessage.Id?
var toPeer: EnginePeer?
@ -230,14 +231,41 @@ private final class StarsTransactionSheetContent: CombinedComponent {
descriptionText = ""
count = subscription.pricing.amount
date = subscription.untilDate
if let creationDate = (subscription.peer._asPeer() as? TelegramChannel)?.creationDate, creationDate > 0 {
additionalDate = creationDate
} else {
additionalDate = nil
}
toPeer = subscription.peer
transactionPeer = .peer(subscription.peer)
isSubscription = true
if subscription.flags.contains(.isCancelled) {
statusText = strings.Stars_Transaction_Subscription_Cancelled
statusIsDestructive = true
buttonText = strings.Stars_Transaction_Subscription_Renew
var hasLeft = false
if let toPeer, case let .channel(channel) = toPeer, channel.participationStatus == .left {
hasLeft = true
}
if hasLeft {
if subscription.flags.contains(.isCancelled) {
statusText = strings.Stars_Transaction_Subscription_Cancelled
statusIsDestructive = true
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
buttonText = strings.Stars_Transaction_Subscription_Renew
} else {
if let _ = subscription.inviteHash {
buttonText = strings.Stars_Transaction_Subscription_JoinAgainChannel
} else {
buttonText = strings.Common_OK
}
}
} else {
if date < Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
buttonText = strings.Stars_Transaction_Subscription_Renew
} else {
statusText = strings.Stars_Transaction_Subscription_LeftChannel(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string
buttonText = strings.Stars_Transaction_Subscription_JoinChannel
}
}
isCancelled = true
} else {
statusText = strings.Stars_Transaction_Subscription_Active(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string
@ -628,16 +656,26 @@ private final class StarsTransactionSheetContent: CombinedComponent {
))
}
if isSubscription, let additionalDate {
tableItems.append(.init(
id: "additionalDate",
title: strings.Stars_Transaction_Subscription_Status_Subscribed,
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: additionalDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
)
))
}
let dateTitle: String
if isSubscription {
if isCancelled {
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
if isCancelled {
dateTitle = strings.Stars_Transaction_Subscription_Status_Expires
} else {
dateTitle = strings.Stars_Transaction_Subscription_Status_Expired
dateTitle = strings.Stars_Transaction_Subscription_Status_Renews
}
} else {
dateTitle = strings.Stars_Transaction_Subscription_Status_Renews
dateTitle = strings.Stars_Transaction_Subscription_Status_Expired
}
} else if isSubscriber {
dateTitle = strings.Stars_Transaction_Subscription_Status_Subscribed
@ -652,6 +690,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
)
))
if isSubscriber, let additionalDate {
tableItems.append(.init(
id: "additionalDate",
title: strings.Stars_Transaction_Subscription_Status_Renews,
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: additionalDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
)
))
}
let table = table.update(
component: TableComponent(
theme: environment.theme,
@ -857,13 +905,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
isLoading: state.inProgress,
action: {
component.cancel(true)
if isSubscription {
if buttonIsDestructive {
component.updateSubscription(.cancel)
} else {
component.updateSubscription(.renew)
}
component.updateSubscription()
}
}
),
@ -899,7 +942,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
let openAppExamples: () -> Void
let copyTransactionId: (String) -> Void
let updateSubscription: (StarsTransactionScreen.SubscriptionAction) -> Void
let updateSubscription: () -> Void
init(
context: AccountContext,
@ -909,7 +952,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
openAppExamples: @escaping () -> Void,
copyTransactionId: @escaping (String) -> Void,
updateSubscription: @escaping (StarsTransactionScreen.SubscriptionAction) -> Void
updateSubscription: @escaping () -> Void
) {
self.context = context
self.subject = subject
@ -1061,7 +1104,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
var openMediaImpl: (([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void)?
var openAppExamplesImpl: (() -> Void)?
var copyTransactionIdImpl: ((String) -> Void)?
var updateSubscriptionImpl: ((StarsTransactionScreen.SubscriptionAction) -> Void)?
var updateSubscriptionImpl: (() -> Void)?
super.init(
context: context,
@ -1083,8 +1126,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
copyTransactionId: { transactionId in
copyTransactionIdImpl?(transactionId)
},
updateSubscription: { action in
updateSubscriptionImpl?(action)
updateSubscription: {
updateSubscriptionImpl?()
}
),
navigationBarAppearance: .none,
@ -1197,27 +1240,37 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
HapticFeedback().tap()
}
updateSubscriptionImpl = { [weak self] action in
updateSubscriptionImpl = { [weak self] in
guard let self, case let .subscription(subscription) = subject, let navigationController = self.navigationController as? NavigationController else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
updateSubscription(action == .cancel)
let title: String
let text: String
switch action {
case .cancel:
title = presentationData.strings.Stars_Transaction_Subscription_Cancelled_Title
text = presentationData.strings.Stars_Transaction_Subscription_Cancelled_Text(subscription.peer.compactDisplayTitle, stringForMediumDate(timestamp: subscription.untilDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)).string
case .renew:
title = presentationData.strings.Stars_Transaction_Subscription_Renewed_Title
text = presentationData.strings.Stars_Transaction_Subscription_Renewed_Text(subscription.peer.compactDisplayTitle).string
var titleAndText: (String, String)?
if subscription.flags.contains(.isCancelled) {
updateSubscription(false)
if subscription.untilDate > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
titleAndText = (
presentationData.strings.Stars_Transaction_Subscription_Renewed_Title,
presentationData.strings.Stars_Transaction_Subscription_Renewed_Text(subscription.peer.compactDisplayTitle).string
)
}
} else {
if subscription.untilDate < Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
updateSubscription(false)
} else {
updateSubscription(true)
titleAndText = (
presentationData.strings.Stars_Transaction_Subscription_Cancelled_Title,
presentationData.strings.Stars_Transaction_Subscription_Cancelled_Text(subscription.peer.compactDisplayTitle, stringForMediumDate(timestamp: subscription.untilDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)).string
)
}
}
let controller = UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: subscription.peer, title: title, text: text, action: nil, duration: 3.0), elevatedLayout: false, position: .bottom, action: { _ in return true })
Queue.mainQueue().after(0.6) {
navigationController.presentOverlay(controller: controller)
if let (title, text) = titleAndText {
let controller = UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: subscription.peer, title: title, text: text, action: nil, duration: 3.0), elevatedLayout: false, position: .bottom, action: { _ in return true })
Queue.mainQueue().after(0.6) {
navigationController.presentOverlay(controller: controller)
}
}
}
}

View File

@ -972,7 +972,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
guard let self else {
return
}
let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: options, purpose: .generic(requiredStars: nil), completion: { [weak self] stars in
let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: options, purpose: .generic, completion: { [weak self] stars in
guard let self else {
return
}

View File

@ -348,8 +348,7 @@ private final class SheetContent: CombinedComponent {
let titleString: String
if isSubscription {
//TODO:localize
titleString = "Subscribe to the Channel"
titleString = strings.Stars_Transfer_Subscribe_Channel_Title
} else {
titleString = strings.Stars_Transfer_Title
}
@ -376,7 +375,7 @@ private final class SheetContent: CombinedComponent {
let amount = component.invoice.totalAmount
let infoText: String
if case .starsChatSubscription = context.component.source {
infoText = "Do you want to subscribe to **\(state.botPeer?.compactDisplayTitle ?? "")** for **\(strings.Stars_Transfer_Info_Stars(Int32(amount)))** per month?"
infoText = strings.Stars_Transfer_SubscribeInfo(state.botPeer?.compactDisplayTitle ?? "", strings.Stars_Transfer_Info_Stars(Int32(amount))).string
} else if !component.extendedMedia.isEmpty {
var description: String = ""
var photoCount: Int32 = 0
@ -403,11 +402,20 @@ private final class SheetContent: CombinedComponent {
description += "**\(strings.Stars_Transfer_SingleVideo)**"
}
}
infoText = strings.Stars_Transfer_UnlockInfo(
description,
state.chatPeer?.compactDisplayTitle ?? "",
strings.Stars_Transfer_Info_Stars(Int32(amount))
).string
if let botPeerName = state.botPeer?.compactDisplayTitle {
infoText = strings.Stars_Transfer_UnlockBotInfo(
description,
botPeerName,
strings.Stars_Transfer_Info_Stars(Int32(amount))
).string
} else {
infoText = strings.Stars_Transfer_UnlockInfo(
description,
state.chatPeer?.compactDisplayTitle ?? "",
strings.Stars_Transfer_Info_Stars(Int32(amount))
).string
}
} else {
infoText = strings.Stars_Transfer_Info(
component.invoice.title,
@ -483,7 +491,7 @@ private final class SheetContent: CombinedComponent {
let amountString = presentationStringsFormattedNumber(Int32(amount), presentationData.dateTimeFormat.groupingSeparator)
let buttonAttributedString: NSMutableAttributedString
if case .starsChatSubscription = component.source {
buttonAttributedString = NSMutableAttributedString(string: "Subscribe", font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
buttonAttributedString = NSMutableAttributedString(string: strings.Stars_Transfer_Subscribe, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
} else {
buttonAttributedString = NSMutableAttributedString(string: "\(strings.Stars_Transfer_Pay) # \(amountString)", font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
}
@ -524,7 +532,7 @@ private final class SheetContent: CombinedComponent {
} else if let peerId = state?.botPeer?.id {
purpose = .transfer(peerId: peerId, requiredStars: invoice.totalAmount)
} else {
purpose = .generic(requiredStars: nil)
purpose = .generic
}
let purchaseController = accountContext.sharedContext.makeStarsPurchaseScreen(
context: accountContext,
@ -549,9 +557,8 @@ private final class SheetContent: CombinedComponent {
var title = presentationData.strings.Stars_Transfer_PurchasedTitle
let text: String
if isSubscription {
//TODO:localize
title = "Subscription successful!"
text = "\(presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))) transferred to \(botTitle)."
title = presentationData.strings.Stars_Transfer_Subscribe_Successful_Title
text = presentationData.strings.Stars_Transfer_Subscribe_Successful_Text(presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount)), botTitle).string
} else if let _ = component.invoice.extendedMedia {
text = presentationData.strings.Stars_Transfer_UnlockedText( presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string
} else {

View File

@ -706,12 +706,38 @@ func openResolvedUrlImpl(
if let navigationController = navigationController {
navigationController.pushViewController(controller, animated: true)
}
case let .starsTopup(amount):
case let .starsTopup(amount, purpose):
dismissInput()
if let starsContext = context.starsContext {
let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: [], purpose: .generic(requiredStars: amount), completion: { _ in })
if let navigationController = navigationController {
navigationController.pushViewController(controller, animated: true)
let proceed = {
let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: [], purpose: .topUp(requiredStars: amount, purpose: purpose), completion: { _ in })
if let navigationController = navigationController {
navigationController.pushViewController(controller, animated: true)
}
}
if let currentState = starsContext.currentState, currentState.balance >= amount {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = UndoOverlayController(
presentationData: presentationData,
content: .universal(
animation: "StarsBuy",
scale: 0.066,
colors: [:],
title: nil,
text: "You have enough stars at the moment.",
customUndoText: "Buy Anyway",
timeout: nil
),
elevatedLayout: true,
action: { action in
if case .undo = action {
proceed()
}
return true
})
present(controller, nil)
} else {
proceed()
}
}
case let .joinVoiceChat(peerId, invite):

View File

@ -920,18 +920,23 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
handleResolvedUrl(.premiumMultiGift(reference: reference))
} else if parsedUrl.host == "stars_topup" {
var amount: Int64?
var purpose: String?
if let components = URLComponents(string: "/?" + query) {
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "amount" {
if queryItem.name == "balance" {
amount = Int64(value)
} else if queryItem.name == "purpose" {
purpose = value
}
}
}
}
}
handleResolvedUrl(.starsTopup(amount: amount))
if let amount {
handleResolvedUrl(.starsTopup(amount: amount, purpose: purpose))
}
} else if parsedUrl.host == "addlist" {
if let components = URLComponents(string: "/?" + query) {
var slug: String?

View File

@ -1650,8 +1650,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return nil
}
public func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController {
return ChatRecentActionsController(context: context, peer: peer, adminPeerId: adminPeerId)
public func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) -> ViewController {
return ChatRecentActionsController(context: context, peer: peer, adminPeerId: adminPeerId, starsState: starsState)
}
public func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) {

View File

@ -1,5 +1,5 @@
{
"app": "10.15",
"app": "11.0",
"xcode": "15.2",
"bazel": "7.1.1",
"macos": "13.0"