From 5ee53ad99671a59a0e57720449819c54a819bb6e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Mar 2025 00:40:11 +0400 Subject: [PATCH] Various fixes --- .../Sources/ContactListNode.swift | 2 +- .../Sources/InAppPurchaseManager.swift | 10 +- .../LegacyComponents/TGMediaEditingContext.h | 2 - .../Sources/EmojiHeaderComponent.swift | 9 +- .../Sources/PremiumIntroScreen.swift | 2 + ...ectivePrivacySettingsPeersController.swift | 2 +- .../TelegramEngine/Messages/SendAsPeers.swift | 9 +- .../Payments/BotPaymentForm.swift | 2 +- .../TelegramEngine/Payments/Stars.swift | 26 ++++ .../PresentationThemeEssentialGraphics.swift | 18 +++ .../Sources/TonFormat.swift | 2 +- .../ChatEmptyNode/Sources/ChatEmptyNode.swift | 25 ++-- .../ChatMessageAnimatedStickerItemNode.swift | 4 + .../ChatMessageAttachedContentNode.swift | 4 + .../Sources/ChatMessageBubbleItemNode.swift | 12 +- .../ChatMessageContactBubbleContentNode.swift | 4 + .../ChatMessageDateAndStatusNode.swift | 122 +++++++++++++++++- ...hatMessageFactCheckBubbleContentNode.swift | 4 + ...ChatMessageGiveawayBubbleContentNode.swift | 1 + .../ChatMessageInteractiveFileNode.swift | 4 + ...atMessageInteractiveInstantVideoNode.swift | 4 + .../ChatMessageInteractiveMediaNode.swift | 4 + .../ChatMessageMapBubbleContentNode.swift | 4 + .../ChatMessageMediaBubbleContentNode.swift | 4 + .../ChatMessagePollBubbleContentNode.swift | 4 + ...atMessageRestrictedBubbleContentNode.swift | 4 + .../Sources/ChatMessageStickerItemNode.swift | 4 + .../ChatMessageTextBubbleContentNode.swift | 4 + .../Sources/ChatUserInfoItem.swift | 2 +- .../Sources/GiftOptionsScreen.swift | 7 +- .../Sources/GiftSetupScreen.swift | 12 +- .../Sources/GiftViewScreen.swift | 30 +++-- .../Sources/PeerInfoGiftsCoverComponent.swift | 10 +- ...oHeaderNavigationButtonContainerNode.swift | 2 +- .../Sources/PeerInfoHeaderNode.swift | 4 + .../Sources/PeerInfoGiftsPaneNode.swift | 9 +- .../Stars/StarsBalanceOverlayComponent/BUILD | 1 + .../StarsBalanceOverlayComponent.swift | 45 ++++++- .../Sources/StarsTransactionScreen.swift | 6 +- .../Sources/StarsTransferScreen.swift | 11 +- .../Message/StarsCount.imageset/Contents.json | 12 ++ .../Message/StarsCount.imageset/msgstar.pdf | Bin 0 -> 4264 bytes .../TelegramUI/Sources/AccountContext.swift | 3 +- .../Chat/ChatControllerOpenWebApp.swift | 17 ++- .../Chat/ChatControllerPaidMessage.swift | 5 +- .../ChatReportPeerTitlePanelNode.swift | 96 ++++++++++---- 46 files changed, 474 insertions(+), 94 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/msgstar.pdf diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index f33c2c5dba..4e29b6eb61 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -591,7 +591,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis allSelected = false } var actionTitle: String? - if peerIds.count > 1 { + if !"".isEmpty, peerIds.count > 1 { actionTitle = allSelected ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : strings.Premium_Gift_ContactSelection_SelectAll.uppercased() } let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(title.uppercased(), AnyHashable(10 * sectionId + (allSelected ? 1 : 0))), theme: theme, strings: strings, actionTitle: actionTitle, action: { _ in diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 0c955d674c..a35fe3344c 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -231,6 +231,8 @@ public final class InAppPurchaseManager: NSObject { private let disposableSet = DisposableDict() + private var lastRequestTimestamp: Double? + public init(engine: TelegramEngine) { self.engine = engine @@ -255,11 +257,15 @@ public final class InAppPurchaseManager: NSObject { productRequest.start() self.productRequest = productRequest + self.lastRequestTimestamp = CFAbsoluteTimeGetCurrent() } public var availableProducts: Signal<[Product], NoError> { - if self.products.isEmpty && self.productRequest == nil { - self.requestProducts() + if self.products.isEmpty { + if let lastRequestTimestamp, CFAbsoluteTimeGetCurrent() - lastRequestTimestamp > 10.0 { + Logger.shared.log("InAppPurchaseManager", "No available products, rerequest") + self.requestProducts() + } } return self.productsPromise.get() } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaEditingContext.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaEditingContext.h index efdee05775..ba44c28837 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaEditingContext.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaEditingContext.h @@ -67,9 +67,7 @@ - (SSignal *)coverImageSignalForItem:(NSObject *)item; - (void)setCoverImage:(UIImage *)image position:(NSNumber *)position forItem:(id)item; - (UIImage *)coverImageForItem:(NSObject *)item; - - (NSNumber *)coverPositionForItem:(NSObject *)item; -- (void)setCoverImage:(UIImage *)image position:(NSNumber *)position forItem:(id)item; - (void)setTemporaryRep:(id)rep forItem:(id)item; diff --git a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift index cba245039f..59dd08228d 100644 --- a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift +++ b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift @@ -64,6 +64,7 @@ class EmojiHeaderComponent: Component { } weak var animateFrom: UIView? + var sourceRect: CGRect? weak var containerView: UIView? let statusView: ComponentHostView @@ -116,8 +117,13 @@ class EmojiHeaderComponent: Component { let initialPosition = self.statusView.center let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView) - let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: 0.0, dy: 0.0) + var sourceOffset: CGPoint = .zero + if let sourceRect = self.sourceRect { + sourceOffset = CGPoint(x: sourceRect.center.x - animateFrom.frame.width / 2.0, y: 0.0) + } + let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: sourceOffset.x, dy: sourceOffset.y) + containerView.addSubview(self.statusView) self.statusView.center = targetPosition @@ -127,6 +133,7 @@ class EmojiHeaderComponent: Component { self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring) Queue.mainQueue().after(0.55, { + self.statusView.layer.removeAllAnimations() self.addSubview(self.statusView) self.statusView.center = initialPosition }) diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 7d0d43370a..88e5de88f5 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -3706,6 +3706,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { } public weak var sourceView: UIView? + public var sourceRect: CGRect? public weak var containerView: UIView? public var animationColor: UIColor? @@ -3884,6 +3885,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { if let sourceView = self.sourceView { view.animateFrom = sourceView + view.sourceRect = self.sourceRect view.containerView = self.containerView view.animateIn() diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift index e18a0ac730..7b724ca498 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift @@ -315,7 +315,7 @@ private func selectivePrivacyPeersControllerEntries(presentationData: Presentati entries.append(.footerItem(footer)) } - if !peers.isEmpty { + if !peers.isEmpty || state.enableForPremium || state.enableForBots { entries.append(.deleteItem(presentationData.strings.Privacy_Exceptions_DeleteAllExceptions)) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift index b9630127c2..01422837b6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift @@ -109,7 +109,14 @@ func _internal_peerSendAsAvailablePeers(accountPeerId: PeerId, network: Network, return .single([]) } - if let channel = peer as? TelegramChannel, case .group = channel.info { + if let channel = peer as? TelegramChannel { + if case .group = channel.info { + + } else if channel.adminRights != nil || channel.flags.contains(.isCreator) { + + } else { + return .single([]) + } } else { return .single([]) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index b5c2e11a66..8817f48613 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -395,7 +395,7 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv var flags: Int32 = 0 var message: Api.TextWithEntities? if let text, !text.isEmpty { - flags |= (1 << 1) + flags |= (1 << 0) message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? []) } return .inputInvoicePremiumGiftStars(flags: flags, userId: inputUser, months: option.months, message: message) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 2587b5fc2b..13c30a2bb6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -568,6 +568,21 @@ private final class StarsContextImpl { self._state = state self._statePromise.set(.single(state)) } + + var onUpdate: Signal { + return self._statePromise.get() + |> take(until: { value in + if let value { + if !value.flags.contains(.isPendingBalance) { + return SignalTakeAction(passthrough: true, complete: true) + } + } + return SignalTakeAction(passthrough: false, complete: false) + }) + |> map { _ in + return Void() + } + } } private extension StarsContext.State.Transaction { @@ -1011,6 +1026,17 @@ public final class StarsContext { } } + public var onUpdate: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.onUpdate.start(next: { value in + subscriber.putNext(value) + })) + } + return disposable + } + } init(account: Account) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index 5143dcad64..a2c2a1dabb 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -170,6 +170,12 @@ public final class PrincipalThemeEssentialGraphics { public let outgoingDateAndStatusRepliesIcon: UIImage public let mediaRepliesIcon: UIImage public let freeRepliesIcon: UIImage + + public let incomingDateAndStatusStarsIcon: UIImage + public let outgoingDateAndStatusStarsIcon: UIImage + public let mediaStarsIcon: UIImage + public let freeStarsIcon: UIImage + public let incomingDateAndStatusPinnedIcon: UIImage public let outgoingDateAndStatusPinnedIcon: UIImage public let mediaPinnedIcon: UIImage @@ -358,6 +364,12 @@ public final class PrincipalThemeEssentialGraphics { self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)! self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)! + let starsImage = UIImage(bundleImageName: "Chat/Message/StarsCount")! + self.incomingDateAndStatusStarsIcon = generateTintedImage(image: starsImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusStarsIcon = generateTintedImage(image: starsImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaStarsIcon = generateTintedImage(image: starsImage, color: .white)! + self.freeStarsIcon = generateTintedImage(image: starsImage, color: serviceColor.primaryText)! + let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")! self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)! self.outgoingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.outgoing.secondaryTextColor)! @@ -479,6 +491,12 @@ public final class PrincipalThemeEssentialGraphics { self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)! self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)! + let starsImage = UIImage(bundleImageName: "Chat/Message/StarsCount")! + self.incomingDateAndStatusStarsIcon = generateTintedImage(image: starsImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusStarsIcon = generateTintedImage(image: starsImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaStarsIcon = generateTintedImage(image: starsImage, color: .white)! + self.freeStarsIcon = generateTintedImage(image: starsImage, color: serviceColor.primaryText)! + let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")! self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)! self.outgoingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.outgoing.secondaryTextColor)! diff --git a/submodules/TelegramStringFormatting/Sources/TonFormat.swift b/submodules/TelegramStringFormatting/Sources/TonFormat.swift index de64f93e82..c237580f34 100644 --- a/submodules/TelegramStringFormatting/Sources/TonFormat.swift +++ b/submodules/TelegramStringFormatting/Sources/TonFormat.swift @@ -81,7 +81,7 @@ public func formatTonAmountText(_ value: Int64, dateTimeFormat: PresentationDate public func formatStarsAmountText(_ amount: StarsAmount, dateTimeFormat: PresentationDateTimeFormat, showPlus: Bool = false) -> String { var balanceText = presentationStringsFormattedNumber(Int32(amount.value), dateTimeFormat.groupingSeparator) - let fraction = Double(amount.nanos) / 10e6 + let fraction = abs(Double(amount.nanos)) / 10e6 if fraction > 0.0 { balanceText.append(dateTimeFormat.decimalSeparator) balanceText.append("\(Int32(fraction))") diff --git a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift index b63ae30563..1478bbb023 100644 --- a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift @@ -1213,9 +1213,9 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE private var currentTheme: PresentationTheme? private var currentStrings: PresentationStrings? - private let stars: StarsAmount? + private let stars: Int64? - public init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?, stars: StarsAmount?) { + public init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?, stars: Int64?) { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) self.isPremiumDisabled = premiumConfiguration.isPremiumDisabled self.stars = stars @@ -1295,7 +1295,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE } ) if let amount = self.stars { - let starsString = presentationStringsFormattedNumber(Int32(amount.value), interfaceState.dateTimeFormat.groupingSeparator) + let starsString = presentationStringsFormattedNumber(Int32(amount), interfaceState.dateTimeFormat.groupingSeparator) let rawText: String if self.isPremiumDisabled { rawText = interfaceState.strings.Chat_EmptyStatePaidMessagingDisabled_Text(peerTitle, " $ \(starsString)").string @@ -1426,7 +1426,7 @@ private enum ChatEmptyNodeContentType: Equatable { case greeting case topic case premiumRequired - case starsRequired + case starsRequired(Int64) } private final class EmptyAttachedDescriptionNode: HighlightTrackingButtonNode { @@ -1815,8 +1815,8 @@ public final class ChatEmptyNode: ASDisplayNode { } else if let _ = interfaceState.peerNearbyData { contentType = .peerNearby } else if let peer = peer as? TelegramUser { - if let _ = interfaceState.sendPaidMessageStars { - contentType = .starsRequired + if let sendPaidMessageStars = interfaceState.sendPaidMessageStars, interfaceState.businessIntro == nil { + contentType = .starsRequired(sendPaidMessageStars.value) } else if interfaceState.isPremiumRequiredForMessaging { contentType = .premiumRequired } else { @@ -1881,8 +1881,8 @@ public final class ChatEmptyNode: ASDisplayNode { node = ChatEmptyNodeTopicChatContent(context: self.context) case .premiumRequired: node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: nil) - case .starsRequired: - node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: interfaceState.sendPaidMessageStars) + case let .starsRequired(stars): + node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: stars) } self.content = (contentType, node) self.addSubnode(node) @@ -1893,8 +1893,13 @@ public final class ChatEmptyNode: ASDisplayNode { node.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction) } } - self.isUserInteractionEnabled = [.peerNearby, .greeting, .premiumRequired, .starsRequired, .cloud].contains(contentType) - + switch contentType { + case .peerNearby, .greeting, .premiumRequired, .starsRequired, .cloud: + self.isUserInteractionEnabled = true + default: + self.isUserInteractionEnabled = false + } + let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom)) var contentSize = CGSize() diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 1064b9d35a..d967016c40 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1044,6 +1044,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var edited = false var viewCount: Int? = nil var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -1057,6 +1058,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -1086,6 +1089,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: messageEffect, replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.message), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 297d1a9980..2df108a4e8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -675,6 +675,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: associatedData.accountPeer, message: message) if message.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) || presentationData.isPreview { dateReactionsAndPeers = ([], []) @@ -688,6 +689,8 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -747,6 +750,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: message), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index ffa7a26e05..6eb157593b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -130,7 +130,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ result.append((message, ChatMessageRestrictedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) needReactions = false break outer - } else if let _ = attribute as? PaidStarsMessageAttribute, !addedPriceInfo { + } else if let _ = attribute as? PaidStarsMessageAttribute, !addedPriceInfo, message.id.peerId.namespace == Namespaces.Peer.CloudUser { result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) addedPriceInfo = true } @@ -299,7 +299,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } if isMediaInverted { - result.insert((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)), at: 0) + result.insert((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)), at: addedPriceInfo ? 1 : 0) } else { result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default))) needReactions = false @@ -327,7 +327,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } if let attribute = message.attributes.first(where: { $0 is WebpagePreviewMessageAttribute }) as? WebpagePreviewMessageAttribute, attribute.leadingPreview { - result.insert((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: 0) + result.insert((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: addedPriceInfo ? 1 : 0) } else { result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) } @@ -362,7 +362,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ if result.isEmpty { needReactions = false } - result.insert((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: 0) + result.insert((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: addedPriceInfo ? 1 : 0) } else { result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) needReactions = false @@ -2276,6 +2276,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: message) if message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -2289,6 +2290,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -2337,6 +2340,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: message), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index e6156462fc..a64006553e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -233,6 +233,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -246,6 +247,8 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -300,6 +303,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: messageEffect, replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 7665ed8d47..f77630f42f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -195,6 +195,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var areReactionsTags: Bool var messageEffect: AvailableMessageEffects.MessageEffect? var replyCount: Int + var starsCount: Int64? var isPinned: Bool var hasAutoremove: Bool var canViewReactionList: Bool @@ -218,6 +219,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { areReactionsTags: Bool, messageEffect: AvailableMessageEffects.MessageEffect?, replyCount: Int, + starsCount: Int64?, isPinned: Bool, hasAutoremove: Bool, canViewReactionList: Bool, @@ -240,6 +242,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { self.areReactionsTags = areReactionsTags self.messageEffect = messageEffect self.replyCount = replyCount + self.starsCount = starsCount self.isPinned = isPinned self.hasAutoremove = hasAutoremove self.canViewReactionList = canViewReactionList @@ -262,7 +265,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { private var repliesIcon: ASImageNode? private var selfExpiringIcon: ASImageNode? private var replyCountNode: TextNode? - + private var starsIcon: ASImageNode? + private var starsCountNode: TextNode? + private var type: ChatMessageDateAndStatusType? private var theme: ChatPresentationThemeData? private var layoutSize: CGSize? @@ -316,12 +321,14 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var currentBackgroundNode = self.backgroundNode var currentImpressionIcon = self.impressionIcon var currentRepliesIcon = self.repliesIcon - + var currentStarsIcon = self.starsIcon + let currentType = self.type let currentTheme = self.theme let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode) - + let makeStarsCountLayout = TextNode.asyncLayout(self.starsCountNode) + let reactionButtonsContainer = self.reactionButtonsContainer return { [weak self] arguments in @@ -337,7 +344,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { let clockMinImage: UIImage? var impressionImage: UIImage? var repliesImage: UIImage? - + var starsImage: UIImage? + let themeUpdated = arguments.presentationData.theme != currentTheme || arguments.type != currentType let graphics = PresentationResourcesChat.principalGraphics(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper, bubbleCorners: arguments.presentationData.chatBubbleCorners) @@ -404,6 +412,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.incomingDateAndStatusPinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.incomingDateAndStatusStarsIcon + } case let .BubbleOutgoing(status): dateColor = arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor outgoingStatus = status @@ -420,6 +431,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.outgoingDateAndStatusPinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.outgoingDateAndStatusStarsIcon + } case .ImageIncoming: dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor backgroundImage = graphics.dateAndStatusMediaBackground @@ -436,6 +450,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.mediaPinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.mediaStarsIcon + } case let .ImageOutgoing(status): dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor outgoingStatus = status @@ -453,6 +470,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.mediaPinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.mediaStarsIcon + } case .FreeIncoming: let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) dateColor = serviceColor.primaryText @@ -471,6 +491,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.freePinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.freeStarsIcon + } case let .FreeOutgoing(status): let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) dateColor = serviceColor.primaryText @@ -489,6 +512,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.freePinnedIcon } + if (arguments.starsCount ?? 0) != 0 { + starsImage = graphics.freeStarsIcon + } } var updatedDateText = arguments.dateText @@ -541,6 +567,20 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { currentRepliesIcon = nil } + var starsIconSize = CGSize() + if let starsImage = starsImage { + if currentStarsIcon == nil { + let iconNode = ASImageNode() + iconNode.isLayerBacked = true + iconNode.displayWithoutProcessing = true + iconNode.displaysAsynchronously = false + currentStarsIcon = iconNode + } + starsIconSize = starsImage.size + } else { + currentStarsIcon = nil + } + if let outgoingStatus = outgoingStatus { switch outgoingStatus { case .Sending: @@ -652,7 +692,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? - + var starsCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? + let reactionSize: CGFloat = 8.0 let reactionSpacing: CGFloat = 2.0 let reactionTrailingSpacing: CGFloat = 6.0 @@ -671,11 +712,29 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { let layoutAndApply = makeReplyCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 + if arguments.starsCount != nil { + reactionInset += 3.0 + } replyCountLayoutAndApply = layoutAndApply } else if arguments.isPinned { reactionInset += 12.0 } + if let starsCount = arguments.starsCount, starsCount > 0 { + let countString: String + if starsCount > 1000000 { + countString = "\(starsCount / 1000000)M" + } else if starsCount > 1000 { + countString = "\(starsCount / 1000)K" + } else { + countString = "\(starsCount)" + } + + let layoutAndApply = makeStarsCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) + reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 + starsCountLayoutAndApply = layoutAndApply + } + if arguments.messageEffect != nil { reactionInset += 13.0 } @@ -1227,6 +1286,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { let replyCountFrame = CGRect(origin: CGPoint(x: reactionOffset + 4.0, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: layout.size) animation.animator.updateFrame(layer: node.layer, frame: replyCountFrame, completion: nil) reactionOffset += 4.0 + layout.size.width + if currentStarsIcon != nil { + reactionOffset += 8.0 + } } else if let replyCountNode = strongSelf.replyCountNode { strongSelf.replyCountNode = nil if animation.isAnimated { @@ -1237,6 +1299,56 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { replyCountNode.removeFromSupernode() } } + + if let currentStarsIcon = currentStarsIcon { + currentStarsIcon.displaysAsynchronously = false + if currentStarsIcon.image !== starsImage { + currentStarsIcon.image = starsImage + } + if currentStarsIcon.supernode == nil { + strongSelf.starsIcon = currentStarsIcon + strongSelf.addSubnode(currentStarsIcon) + if animation.isAnimated { + currentStarsIcon.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + let starsIconFrame = CGRect(origin: CGPoint(x: reactionOffset - 2.0, y: backgroundInsets.top + offset + verticalInset + floor((date.size.height - starsIconSize.height) / 2.0)), size: starsIconSize) + animation.animator.updateFrame(layer: currentStarsIcon.layer, frame: starsIconFrame, completion: nil) + reactionOffset += 9.0 + } else if let starsIcon = strongSelf.starsIcon { + strongSelf.starsIcon = nil + if animation.isAnimated { + starsIcon.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak starsIcon] _ in + starsIcon?.removeFromSupernode() + }) + } else { + starsIcon.removeFromSupernode() + } + } + + if let (layout, apply) = starsCountLayoutAndApply { + let node = apply() + if strongSelf.starsCountNode !== node { + strongSelf.starsCountNode?.removeFromSupernode() + strongSelf.addSubnode(node) + strongSelf.starsCountNode = node + if animation.isAnimated { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + let starsCountFrame = CGRect(origin: CGPoint(x: reactionOffset + 4.0, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: layout.size) + animation.animator.updateFrame(layer: node.layer, frame: starsCountFrame, completion: nil) + reactionOffset += 4.0 + layout.size.width + } else if let starsCountNode = strongSelf.starsCountNode { + strongSelf.starsCountNode = nil + if animation.isAnimated { + starsCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak starsCountNode] _ in + starsCountNode?.removeFromSupernode() + }) + } else { + starsCountNode.removeFromSupernode() + } + } } }) }) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageFactCheckBubbleContentNode/Sources/ChatMessageFactCheckBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageFactCheckBubbleContentNode/Sources/ChatMessageFactCheckBubbleContentNode.swift index a2e2b5deef..a41e7a8686 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageFactCheckBubbleContentNode/Sources/ChatMessageFactCheckBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageFactCheckBubbleContentNode/Sources/ChatMessageFactCheckBubbleContentNode.swift @@ -295,6 +295,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode var rawText = "" var rawEntities: [MessageTextEntity] = [] var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -311,6 +312,8 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -447,6 +450,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index e939dc18e6..263522c344 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -578,6 +578,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: nil, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index 5357300699..88c6dd9b03 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -898,6 +898,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: arguments.context.account.peerId, accountPeer: arguments.associatedData.accountPeer, message: arguments.topMessage) if arguments.topMessage.isRestricted(platform: "ios", contentSettings: arguments.context.currentContentSettings.with { $0 }) || arguments.presentationData.isPreview { dateReactionsAndPeers = ([], []) @@ -911,6 +912,8 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { if let channel = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, arguments.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } if arguments.forcedIsEdited { @@ -956,6 +959,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { areReactionsTags: arguments.message.areReactionsTags(accountPeerId: arguments.context.account.peerId), messageEffect: arguments.message.messageEffect(availableMessageEffects: arguments.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: arguments.isPinned && !arguments.associatedData.isInPinnedListMode, hasAutoremove: arguments.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: arguments.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index a55f886df6..f95da1047c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -524,6 +524,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let sentViaBot = false var viewCount: Int? = nil var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -537,6 +538,8 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -583,6 +586,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: messageEffect, replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index 5946804836..468ea63fdf 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -83,6 +83,7 @@ public struct ChatMessageDateAndStatus { public var dateReactions: [MessageReaction] public var dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)] public var dateReplies: Int + public var starsCount: Int64? public var isPinned: Bool public var dateText: String @@ -93,6 +94,7 @@ public struct ChatMessageDateAndStatus { dateReactions: [MessageReaction], dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)], dateReplies: Int, + starsCount: Int64?, isPinned: Bool, dateText: String ) { @@ -102,6 +104,7 @@ public struct ChatMessageDateAndStatus { self.dateReactions = dateReactions self.dateReactionPeers = dateReactionPeers self.dateReplies = dateReplies + self.starsCount = starsCount self.isPinned = isPinned self.dateText = dateText } @@ -1118,6 +1121,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), messageEffect: messageEffect, replyCount: dateAndStatus.dateReplies, + starsCount: dateAndStatus.starsCount, isPinned: dateAndStatus.isPinned, hasAutoremove: message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: message), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index 2c8c18860a..a9511b99c1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -192,6 +192,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -205,6 +206,8 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -284,6 +287,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift index 49392be769..8f8397d26e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift @@ -307,6 +307,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -323,6 +324,8 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -373,6 +376,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { dateReactions: dateReactionsAndPeers.reactions, dateReactionPeers: dateReactionsAndPeers.peers, dateReplies: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, dateText: dateText ) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index 332531f153..0017cedcf4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1054,6 +1054,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -1067,6 +1068,8 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -1125,6 +1128,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift index 8d5e633293..01a336ad60 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -56,6 +56,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod var viewCount: Int? var rawText = "" var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -71,6 +72,8 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -140,6 +143,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index c968aa1a50..e2d44addfe 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -602,6 +602,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { var edited = false var viewCount: Int? = nil var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -615,6 +616,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -648,6 +651,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.message), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index f6e820a9af..1e0dd952d7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -264,6 +264,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 + var starsCount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.topMessage) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -278,6 +279,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { dateReplies = Int(attribute.count) } + } else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + starsCount = attribute.stars.value } } @@ -647,6 +650,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, + starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread), hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.topMessage), diff --git a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift index 9bf11f830f..c7beb06383 100644 --- a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift @@ -393,7 +393,7 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe let disclaimerText: NSMutableAttributedString if let verification = item.verification { - disclaimerText = NSMutableAttributedString(string: " # \(verification.description)", font: Font.regular(13.0), textColor: subtitleColor) + disclaimerText = NSMutableAttributedString(string: " # \(verification.description)", font: Font.regular(13.0), textColor: subtitleColor) if let range = disclaimerText.string.range(of: "#") { disclaimerText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSRange(range, in: disclaimerText.string)) disclaimerText.addAttribute(.foregroundColor, value: subtitleColor, range: NSRange(range, in: disclaimerText.string)) diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index b684b61345..ef5ec71c1e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -1237,6 +1237,11 @@ final class GiftOptionsScreenComponent: Component { if availableProducts.isEmpty { var premiumProducts: [PremiumGiftProduct] = [] for option in premiumOptions { + if option.currency == "XTR" { + continue + } + let starsGiftOption = premiumOptions.first(where: { $0.currency == "XTR" && $0.months == option.months }) + premiumProducts.append( PremiumGiftProduct( giftOption: CachedPremiumGiftOption( @@ -1246,7 +1251,7 @@ final class GiftOptionsScreenComponent: Component { botUrl: "", storeProductId: option.storeProductId ), - starsGiftOption: nil, + starsGiftOption: starsGiftOption, storeProduct: nil, discount: nil ) diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index 56549057c4..ece1fa0f97 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -498,16 +498,8 @@ final class GiftSetupScreenComponent: Component { starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) - let _ = (starsContext.state - |> take(until: { value in - if let value { - if !value.flags.contains(.isPendingBalance) { - return SignalTakeAction(passthrough: true, complete: true) - } - } - return SignalTakeAction(passthrough: false, complete: false) - }) - |> deliverOnMainQueue).start(next: { _ in + let _ = (starsContext.onUpdate + |> deliverOnMainQueue).start(next: { proceed() }) } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 1ca31db8c7..da56c538e7 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -351,8 +351,12 @@ private final class GiftViewSheetContent: CombinedComponent { self.inProgress = true self.updated() + if let controller = self.getController() as? GiftViewScreen { + controller.showBalance = false + } + self.upgradeDisposable = (self.upgradeGift(formId, self.keepOriginalInfo) - |> deliverOnMainQueue).start(next: { [weak self] result in + |> deliverOnMainQueue).start(next: { [weak self, weak starsContext] result in guard let self, let controller = self.getController() as? GiftViewScreen else { return } @@ -363,6 +367,10 @@ private final class GiftViewSheetContent: CombinedComponent { controller.subject = self.subject controller.animateSuccess() self.updated(transition: .spring(duration: 0.4)) + + Queue.mainQueue().after(0.5) { + starsContext?.load(force: true) + } }) } @@ -382,11 +390,18 @@ private final class GiftViewSheetContent: CombinedComponent { starsContext: starsContext, options: options ?? [], purpose: .upgradeStarGift(requiredStars: price), - completion: { [weak starsContext] stars in - starsContext?.add(balance: StarsAmount(value: stars, nanos: 0)) - Queue.mainQueue().after(2.0) { - proceed(upgradeForm.id) + completion: { [weak self, weak starsContext] stars in + guard let self, let starsContext else { + return } + self.inProgress = true + self.updated() + + starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) + let _ = (starsContext.onUpdate + |> deliverOnMainQueue).start(next: { + proceed(upgradeForm.id) + }) } ) controller.push(purchaseController) @@ -2226,9 +2241,8 @@ private final class GiftViewSheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - 16.0 - buttons.size.width / 2.0, y: 28.0)) ) - let contentSize = CGSize(width: context.availableSize.width, height: originY + 5.0 + environment.safeInsets.bottom) - - return contentSize + let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom + return CGSize(width: context.availableSize.width, height: originY + 5.0 + effectiveBottomInset) } } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift index 6c81f40c57..9b7bc718ff 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift @@ -19,6 +19,7 @@ public final class PeerInfoGiftsCoverComponent: Component { public let giftsContext: ProfileGiftsContext public let hasBackground: Bool public let avatarCenter: CGPoint + public let avatarSize: CGSize public let defaultHeight: CGFloat public let avatarTransitionFraction: CGFloat public let statusBarHeight: CGFloat @@ -34,6 +35,7 @@ public final class PeerInfoGiftsCoverComponent: Component { giftsContext: ProfileGiftsContext, hasBackground: Bool, avatarCenter: CGPoint, + avatarSize: CGSize, defaultHeight: CGFloat, avatarTransitionFraction: CGFloat, statusBarHeight: CGFloat, @@ -48,6 +50,7 @@ public final class PeerInfoGiftsCoverComponent: Component { self.giftsContext = giftsContext self.hasBackground = hasBackground self.avatarCenter = avatarCenter + self.avatarSize = avatarSize self.defaultHeight = defaultHeight self.avatarTransitionFraction = avatarTransitionFraction self.statusBarHeight = statusBarHeight @@ -71,6 +74,9 @@ public final class PeerInfoGiftsCoverComponent: Component { if lhs.avatarCenter != rhs.avatarCenter { return false } + if lhs.avatarSize != rhs.avatarSize { + return false + } if lhs.defaultHeight != rhs.defaultHeight { return false } @@ -213,14 +219,14 @@ public final class PeerInfoGiftsCoverComponent: Component { } excludeRects.append(CGRect(origin: CGPoint(x: 0.0, y: component.statusBarHeight), size: component.topLeftButtonsSize)) excludeRects.append(CGRect(origin: CGPoint(x: availableSize.width - component.topRightButtonsSize.width, y: component.statusBarHeight), size: component.topRightButtonsSize)) - excludeRects.append(CGRect(origin: CGPoint(x: floor((availableSize.width - component.titleWidth) / 2.0), y: avatarCenter.y + 56.0), size: CGSize(width: component.titleWidth, height: 72.0))) + excludeRects.append(CGRect(origin: CGPoint(x: floor((availableSize.width - component.titleWidth) / 2.0), y: avatarCenter.y + component.avatarSize.height / 2.0 + 6.0), size: CGSize(width: component.titleWidth, height: 100.0))) if component.bottomHeight > 0.0 { excludeRects.append(CGRect(origin: CGPoint(x: 0.0, y: component.defaultHeight - component.bottomHeight), size: CGSize(width: availableSize.width, height: component.bottomHeight))) } let positionGenerator = PositionGenerator( containerSize: CGSize(width: availableSize.width, height: component.defaultHeight), - centerFrame: CGSize(width: 100, height: 100).centered(around: avatarCenter), + centerFrame: component.avatarSize.centered(around: avatarCenter), exclusionZones: excludeRects, minimumDistance: 42.0, edgePadding: 5.0, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index 3b08ca63bd..bba2f02c8e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -257,7 +257,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { for key in removeKeys { if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { if key == .moreSearchSort || key == .searchWithTags || key == .standaloneSearch { - buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in + buttonNode.layer.animateAlpha(from: buttonNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in buttonNode?.removeFromSupernode() }) buttonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 1c0c8c45df..b1daf1a100 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -2289,6 +2289,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { } if !buttonKeys.isEmpty { backgroundDefaultHeight = 327.0 + if metrics.isTablet { + backgroundDefaultHeight += 60.0 + } } hasBackground = true } else if let peer { @@ -2352,6 +2355,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { giftsContext: profileGiftsContext, hasBackground: hasBackground, avatarCenter: apparentAvatarFrame.center, + avatarSize: apparentAvatarFrame.size, defaultHeight: backgroundDefaultHeight, avatarTransitionFraction: max(0.0, min(1.0, titleCollapseFraction + transitionFraction * 2.0)), statusBarHeight: statusBarHeight, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index e924b19dd6..c394a58b4e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -677,13 +677,14 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let buttonSideInset = sideInset + 16.0 let buttonSize = CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0) - var bottomPanelHeight = max(8.0, bottomInset) + buttonSize.height + 8.0 + let effectiveBottomInset = max(8.0, bottomInset) + var bottomPanelHeight = effectiveBottomInset + buttonSize.height + 8.0 if params.visibleHeight < 110.0 { scrollOffset -= bottomPanelHeight } let panelTransition = ComponentTransition.immediate - panelTransition.setFrame(view: panelButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - buttonSize.height - scrollOffset), size: buttonSize)) + panelTransition.setFrame(view: panelButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - effectiveBottomInset - buttonSize.height - scrollOffset), size: buttonSize)) panelTransition.setAlpha(view: panelButton.view, alpha: panelAlpha) let _ = panelButton.updateLayout(width: buttonSize.width, transition: .immediate) @@ -754,7 +755,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr if panelCheckView.superview == nil { self.view.addSubview(panelCheckView) } - panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: size.height - bottomInset - panelCheckSize.height - 11.0 - scrollOffset), size: panelCheckSize) + panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: size.height - effectiveBottomInset - panelCheckSize.height - 11.0 - scrollOffset), size: panelCheckSize) panelTransition.setAlpha(view: panelCheckView, alpha: panelAlpha) } panelButton.isHidden = true @@ -998,7 +999,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr }))) } - if canReorder { + if case .unique = gift.gift, canReorder { items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_Reorder, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in c?.dismiss(completion: { [weak self] in guard let self else { diff --git a/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/BUILD b/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/BUILD index 7e5550cac0..006c97027a 100644 --- a/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/BUILD +++ b/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/AsyncDisplayKit", "//submodules/Display", "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", "//submodules/AccountContext", "//submodules/ComponentFlow", "//submodules/Components/MultilineTextComponent", diff --git a/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/Sources/StarsBalanceOverlayComponent.swift b/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/Sources/StarsBalanceOverlayComponent.swift index dec416f208..c304e8ca32 100644 --- a/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/Sources/StarsBalanceOverlayComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent/Sources/StarsBalanceOverlayComponent.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import ComponentFlow +import SwiftSignalKit import TelegramCore import AccountContext import TelegramPresentationData @@ -38,6 +39,10 @@ public final class StarsBalanceOverlayComponent: Component { private let action = ComponentView() private var component: StarsBalanceOverlayComponent? + private var state: EmptyComponentState? + + private var balance: Int64 = 0 + private var balanceDisposable: Disposable? private var cachedChevronImage: (UIImage, PresentationTheme)? @@ -53,17 +58,43 @@ public final class StarsBalanceOverlayComponent: Component { fatalError("init(coder:) has not been implemented") } + deinit { + self.balanceDisposable?.dispose() + } + @objc private func tapped() { if let component = self.component { component.action() } } + private var isUpdating = false func update(component: StarsBalanceOverlayComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } self.component = component + if self.balanceDisposable == nil, let starsContext = component.context.starsContext { + self.balanceDisposable = (starsContext.state + |> map { state -> Int64 in + return state?.balance.value ?? 0 + } + |> distinctUntilChanged + |> deliverOnMainQueue).start(next: { [weak self] balance in + guard let self else { + return + } + self.balance = balance + if !self.isUpdating { + self.state?.updated() + } + }) + } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - let balance = presentationStringsFormattedNumber(Int32(component.context.starsContext?.currentState?.balance.value ?? 0), presentationData.dateTimeFormat.groupingSeparator) + let balance = presentationStringsFormattedNumber(Int32(self.balance), presentationData.dateTimeFormat.groupingSeparator) let attributedText = parseMarkdownIntoAttributedString( presentationData.strings.StarsBalance_YourBalance("**⭐️\(balance)**").string, @@ -121,23 +152,27 @@ public final class StarsBalanceOverlayComponent: Component { if let textView = self.text.view { if textView.superview == nil { - self.addSubview(textView) + self.backgroundView.addSubview(textView) } textView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: 10.0), size: textSize) } if let actionView = self.action.view { if actionView.superview == nil { - self.addSubview(actionView) + self.backgroundView.addSubview(actionView) } actionView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - actionSize.width) / 2.0), y: 29.0), size: actionSize) } self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.opaqueBackgroundColor, transition: .immediate) self.backgroundView.update(size: size, cornerRadius: size.height / 2.0, transition: .immediate) - transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size)) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - size.width) / 2.0), y: 0.0), size: size)) - return size + return CGSize(width: availableSize.width, height: size.height) + } + + public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + return self.backgroundView.frame.contains(point) } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index b4066cab07..96093e6a99 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -1530,15 +1530,15 @@ private final class StarsTransactionSheetContent: CombinedComponent { .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) ) originY += button.size.height + originY += 7.0 } context.add(closeButton .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0)) ) - let contentSize = CGSize(width: context.availableSize.width, height: originY + 5.0 + environment.safeInsets.bottom) - - return contentSize + let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom + return CGSize(width: context.availableSize.width, height: originY + 5.0 + effectiveBottomInset) } } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index 46cbbfaf0a..d556d7a17c 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -589,10 +589,15 @@ private final class SheetContent: CombinedComponent { options: state?.options ?? [], purpose: purpose, completion: { [weak starsContext] stars in - starsContext?.add(balance: StarsAmount(value: stars, nanos: 0)) - Queue.mainQueue().after(0.1) { - completion() + guard let starsContext else { + return } + starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) + + let _ = (starsContext.onUpdate + |> deliverOnMainQueue).start(next: { + completion() + }) } ) controller?.push(purchaseController) diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/Contents.json new file mode 100644 index 0000000000..fa834c36d7 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "msgstar.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/msgstar.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/StarsCount.imageset/msgstar.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5565014e2c1a06bad09ab7db408d9b2e8cdb0573 GIT binary patch literal 4264 zcmai1c|6oz7dJ7MiDZco4N_zogBd0z`;u))vNjDy#x|J2WZw#p$P&hqHIYcB$WD}{ zQI?WDL`5R|7I|m1Ja5nQem?L0=icAcz`@xj_-Om)<$BIV`(z#>`E1DV(f7an4b~@jQbUW6Ic!g zSNw?>f#sYb;N^U;c$?{WP}k2G_wg~1-(#G`b*$s8U0bhXT!ex7t7u? z7S{#`e$E{Jl=UrlmoDC&UQ^bqfcbgeWt6d>rUSR&Is;2bIKs1v4?8{v3~ccYU&i?! zCfCM{q_weqJ3ugdCa}iCX<5IE{8rCR+-8*_XYw|WSWHbaXRDHU1o%7Es|4vzBfcy1 z1h&<23$fERUpN+gW!pv?s%xGhrN;s8O!5R}Cxv>zo43mKuyzrW7|))7?^wQ=%@Kx( zT8`sa&TZ%ln!r;9E$u8-B-B${47oa#_|Q!5`2@B_(dVEm^xR z9j_d+R&MSCXYW^CmVTgl3vpC~;(l0F)941{n-HFE`qmJDYY4pu<9w6AM!4!hkjPW# zCLjwK7^TWHKvx-d8cDYnvXgV<2mqFIKp{jv2lzNVW*k%na1RH_vfjDOTE}pHT#ytB zW@a)0pS;6})nJV0bD)O`Uu+k!+6UA~K(cbPvk0?11`IJCQhkm@m;w9L&T4$hiRp$F zvDdK2s&aLsx=$PiCZYLDSOl6=WVzNt=*N-7Lr2&PTV}^?h>A9h7h4L)9XCV*=!HTG z7W9eyK;*GopmR*mRiP$QX5f>cLy?@h@Z2Snf+A-#`eL@e@S5<%yyGTEdn^uSJoTqx;UN6RfcXQ&~G zLIQLi{wT;3bKxmJTJT}efUc{E&zZ`D{>;NsbI;b$I_0mGju^3BxsHDde6OSgm0|Zi z=nL`%^xqU{32aJQ7$OSPv8#X}&pwUoFP>Nw+z8snZ?md}URL!GVc;Z2R78lVv*pHm z#K>xsgimtDM#(%w+KG5dmfsjqtuqMF4bTutAn3*2`EU$?-GA=}A)Sz{puVMRE|QUG zIpsu-sLKe*9MNkq4${a)vP#U~690=t&M?)f*1T%43D!w|6mL&ioPtv>w8JQ?l={RI z?E>xo?PV#b6R4BH=HFAc@9a~1iVQ7iG8wpo%=Iur>!Pg^ik(Ui7m@NTiioC*>F=vJ zjXIDW*3pHx?h_1V5_`oDY4zoki)=o_o!M69yl-4D;OiN9tZ4<9J7EQJJ7=jkXpS&I zSXZlW){)JC1$ITv=6dE5rigsGq9ttT$9*?}+rU~L1*vJ&!X@wA;@Xoa7^=d=TXi*w z-}a@ow4Pg;Rm_m6_v4y-TVAVDt1X{X`%duo@|=Q9$#kNKjuZ537A3hY>4+Usylf`XvM*guRcSiAU}7q zP_*Y>?@V7>53JWVHzR+z=$r1%qP90NuO)kCdoz0b-=c~e)fzADh60|3VB;@)H0897 z*XJgnt0oS#@U(b}UJ$M9IhF61AKA)nuQ*AVbe^m|Zozd|FR?>7Y54BLd$+M+*lRP= z@&ldk3@-b$mvkMmpLdc`D1#rhi{}%%VW54+{+ar7yJ)RAt(fPM`hgU2N`1BaUeLLbUpVR5=F@i5XwAs3P~Ir`lze7Vrtztp z5V%z*%H|RAjaL7)Nz)G^-UYR-RYKm$H8WM7BgLaxHM7;rBQB%a{v_ftzsx1#2kZxl zCD)~ZE-yPb2XDXJx`6tndcXSQx{JhA|Fdg7tI_NC*3*_MmTkWd&qOZnUl^WMeD>A> zKj{5MBA976_WLMcDkMH+DWp)}AT=uGvGldnEy(DTl?l0UOfyD06dW&D1z9kOH$nP! zmr=?o;VBoTf_Yp8>x8RLMDUZ3PdT*LBD=ctx+e~`N*F)2YN`i-D$>dl&BvPAyEeatE9@rGMNtx19S(wQM!m+Z1-%k@dW z6~Fnd2W+PVD%}qcPM4QEb3K;uIp6PC|FSxxt#ZR1QJ!1{!_MLm=7{GA+>>iK7fN5} zXrgk>sIPxumQ9v8&M9&^PE@%^8K~^HF%w`(eFdUDrmMXU|&fBG+2J z%vX)pYprQTu3uhfUT2;69`?_(C@0!3PB#>MOV^(k+Z4mCmkkD&zNuVk2fPP#0I-9J zupRPYSR{YVq<#Vt7 zfPGq9R$oNl%69v9`2+L%%A7Td=Y;1>^f=e3_W^J36EGv5zLwR*t?2VTh3>mktLI(l z^K*+$3d+Y7l%x?m?>~{UnYtJ)Y7dNxYMgIG@rP{swN(8Ta+2`F9jR zYT2d5YOi~3`Jb9Eq~4$F&wOyZ_Ep_Gua@uH%Z9ZXhK++eZ{N6#4F^yr%xB%&hTQ^V zzMa|{-GaPZtigYf`8FRLilGk-mt_3Prp2!Jb#Xp+%(!89OYLKL+JrPalzsWA#OafP zC5u&C(H%+J_v%-+qx#!l7780j>Qv`Lg1a_$mM!MxUp(slG}JZQ>ef1ZBH*}tqs6A( z7t9bPt)uQN_ij-DX8UvM+0orP#33kb&1%JOt*lwm85h@B5jYGJRM^?DeNmgXzIpVG zO6ErU_Q0~l_Uq-elCG*Q49cM4aUk2qmIoZ;v!={$_jJv%XRcImZjKMm>QcjdHcnKJ z=QZY4TylEmNsbU_EG| zs8R2MkQ-H6aJ;^n!!uF)tj<>rPzJh-`>W#X9zAxLn?=Gd%of6eZzRiW)oj-8c^yTEX)2lfbU)6AUaRr!&N4gyNa{pY7TUc^)&N*|2K9-HK z^rlB=!mT`|Ep7eyOH2DZxgOrU!S?oZZ9j|Y!dM~dzFir=!xOJKv1q~v>r$)M(<-qq zj!!#n9CUg>J|+$^SI;R4Pn0*Vb~&*_f(EpN^2J_dq|6Dn9*?Kv0rzREM3JOx5i6DI z=Z_`(j50pGFjM@7U%9CNrC;io(I*-r)9l>Ce9=J4$EIV+j~<|}w_9@x+A}8TlDT_1 zub4g>(m9NB&ZHOqY@!RX7QjA*yW}@qkn+q%HfXx`!ECV1;xwu11zP@J7RdPGiZnPOZzh zx6KXN^v4BMeVP)0pKZ`Ja+)?o&Tv*S)+;=!JOUT%`eq~g#EVA?h!pD;F0jOd7iSE8 z8ouTGcJ_p2AxLPU;f-k)M>$7E>(HdFl9>G|+fll8;0mUhFBNLd7so2n$zkYnAnNH+ zV_DBFfx}}Rl2w1%eTR}fT~tN%2N>amH+%)-1@~Ouv2rgoD4UlzsTuGFmAFLek9_4& zmt7Ul)H}-^6#S%B)+X-`gYgZ?N4XCjJpzNYg95(_we7Yw1|LDiHQvOjeBpTcKdP7Z zyZ>k&6`=~hYew-my4O;=yWa$}_v_LG3P)=qtb4M?;JQ7OjpWf~~2gx>u>P ze=neNzo5TYzbu(z@HlTTdkh{-dkAP{N9$8HaP9<*JJlntbboTlqw1fQ!Tmwx&yW+0=xs~XJ zaRkYNp|YSK@$WrXa2QM;2BzXa2b7wYJ%RgANDhvm9`sL0QSM(_iZE)7e`+bg{<%?} z>g^v|@^Hm}X(_?}X deliverOnMainQueue).start(next: { value in let _ = currentCountriesConfiguration.swap(CountriesConfiguration(countries: value)) }) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index 3477609073..583e2babda 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -13,6 +13,7 @@ import PresentationDataUtils import UndoUI import UrlHandling import TelegramPresentationData +import ChatInterfaceState func openWebAppImpl( context: AccountContext, @@ -182,7 +183,7 @@ func openWebAppImpl( var isInline = false var botId = botPeer.id var botName = botName - var botAddress = "" + var botAddress = botPeer.addressName ?? "" var botVerified = botPeer.isVerified if case let .inline(bot) = source { isInline = true @@ -367,8 +368,20 @@ public extension ChatControllerImpl { } } } + let inputString = "@\(botAddress) \(query)" if let chatController { - chatController.controllerInteraction?.activateSwitchInline(selectedPeer?.id ?? peerId, "@\(botAddress) \(query)", nil) + chatController.controllerInteraction?.activateSwitchInline(selectedPeer?.id ?? peerId, inputString, nil) + } else if let selectedPeer, let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController { + let textInputState = ChatTextInputState(inputText: NSAttributedString(string: inputString)) + let _ = (ChatInterfaceState.update(engine: context.engine, peerId: selectedPeer.id, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) + }) + |> deliverOnMainQueue).startStandalone(completed: { [weak navigationController] in + guard let navigationController else { + return + } + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(selectedPeer), subject: nil, updateTextInputState: textInputState, peekData: nil)) + }) } } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift index cae3d5b3d9..16d06cfde1 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift @@ -72,7 +72,10 @@ extension ChatControllerImpl { return } let controller = self.context.sharedContext.makeStarsPurchaseScreen(context: self.context, starsContext: starsContext, options: options, purpose: .sendMessage(peerId: peer.id, requiredStars: totalAmount), completion: { _ in - completion(false) + let _ = (starsContext.onUpdate + |> deliverOnMainQueue).start(next: { + completion(false) + }) }) self.push(controller) }) diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 7eac909522..adedeb9680 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -3,6 +3,7 @@ import UIKit import Display import AsyncDisplayKit import Postbox +import SwiftSignalKit import TelegramCore import TelegramPresentationData import LocalizedPeerData @@ -14,6 +15,7 @@ import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer import AccountContext +import PremiumUI private enum ChatReportPeerTitleButton: Equatable { case block @@ -344,7 +346,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { private let context: AccountContext private let animationCache: AnimationCache private let animationRenderer: MultiAnimationRenderer - + private let separatorNode: ASDisplayNode private let closeButton: HighlightableButtonNode @@ -354,12 +356,17 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { private let emojiSeparatorNode: ASDisplayNode private var theme: PresentationTheme? + private var presentationInterfaceState: ChatPresentationInterfaceState? private var inviteInfoNode: ChatInfoTitlePanelInviteInfoNode? private var peerNearbyInfoNode: ChatInfoTitlePanelPeerNearbyInfoNode? private var cachedChevronImage: (UIImage, PresentationTheme)? + private var emojiStatusPackDisposable = MetaDisposable() + private var emojiStatusFileId: Int64? + private var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>() + private var tapGestureRecognizer: UITapGestureRecognizer? init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) { @@ -391,6 +398,10 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { self.addSubnode(self.closeButton) } + deinit { + self.emojiStatusPackDisposable.dispose() + } + override func didLoad() { super.didLoad() @@ -405,27 +416,33 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { } private func openPremiumEmojiStatusDemo() { - guard let navigationController = self.interfaceInteraction?.getNavigationController() else { + guard let navigationController = self.interfaceInteraction?.getNavigationController(), let peerId = self.presentationInterfaceState?.chatLocation.peerId, let emojiStatus = self.presentationInterfaceState?.renderedPeer?.peer?.emojiStatus, case let .emoji(fileId) = emojiStatus.content else { return } - if self.context.isPremium { - let controller = context.sharedContext.makePremiumIntroController(context: self.context, source: .animatedEmoji, forceDark: false, dismissed: nil) - navigationController.pushViewController(controller) - } else { - var replaceImpl: ((ViewController) -> Void)? - let controller = self.context.sharedContext.makePremiumDemoController(context: self.context, subject: .emojiStatus, forceDark: false, action: { [weak self] in - guard let self else { - return - } - let controller = context.sharedContext.makePremiumIntroController(context: self.context, source: .animatedEmoji, forceDark: false, dismissed: nil) - replaceImpl?(controller) - }, dismissed: nil) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + let source: Signal = self.emojiStatusFileAndPackTitle.get() + |> take(1) + |> mapToSignal { emojiStatusFileAndPack -> Signal in + if let (file, pack) = emojiStatusFileAndPack { + return .single(.emojiStatus(peerId, fileId, file, pack)) + } else { + return .complete() } - navigationController.pushViewController(controller) } + + let _ = (source + |> deliverOnMainQueue).startStandalone(next: { [weak self, weak navigationController] source in + guard let self, let navigationController else { + return + } + let controller = PremiumIntroScreen(context: self.context, source: source) + if let textView = self.emojiStatusTextNode?.view { + controller.sourceView = textView + controller.sourceRect = CGRect(origin: .zero, size: CGSize(width: textView.frame.height, height: textView.frame.height)) + } + controller.containerView = navigationController.view + navigationController.pushViewController(controller) + }) } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { @@ -436,7 +453,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor self.emojiSeparatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor } - + self.presentationInterfaceState = interfaceState + var panelHeight: CGFloat = 40.0 let contentRightInset: CGFloat = 14.0 + rightInset @@ -583,11 +601,43 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { } } - /*#if DEBUG - emojiStatus = PeerEmojiStatus(fileId: 5062172592505356289, expirationDate: nil) - #endif*/ - - if let emojiStatus = emojiStatus { + if let emojiStatus = emojiStatus, case let .emoji(fileId) = emojiStatus.content { + if self.emojiStatusFileId != fileId { + self.emojiStatusFileId = fileId + + let emojiFileAndPack = self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) + |> mapToSignal { result in + if let emojiFile = result.first?.value { + for attribute in emojiFile.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference { + return self.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) + |> filter { result in + if case .result = result { + return true + } else { + return false + } + } + |> mapToSignal { result -> Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError> in + if case let .result(_, items, _) = result { + return .single(items.first.flatMap { ($0.file._parse(), result) }) + } else { + return .complete() + } + } + } + } + } + return .complete() + } + self.emojiStatusPackDisposable.set(emojiFileAndPack.startStrict(next: { [weak self] fileAndPackTitle in + guard let self else { + return + } + self.emojiStatusFileAndPackTitle.set(.single(fileAndPackTitle)) + })) + } + self.emojiSeparatorNode.isHidden = false transition.updateFrame(node: self.emojiSeparatorNode, frame: CGRect(origin: CGPoint(x: leftInset + 12.0, y: 40.0), size: CGSize(width: width - leftInset - rightInset - 24.0, height: UIScreenPixel)))