Various fixes

This commit is contained in:
Ilya Laktyushin 2025-03-12 00:40:11 +04:00
parent 21c9965750
commit 5ee53ad996
46 changed files with 474 additions and 94 deletions

View File

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

View File

@ -231,6 +231,8 @@ public final class InAppPurchaseManager: NSObject {
private let disposableSet = DisposableDict<String>()
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()
}

View File

@ -67,9 +67,7 @@
- (SSignal *)coverImageSignalForItem:(NSObject<TGMediaEditableItem> *)item;
- (void)setCoverImage:(UIImage *)image position:(NSNumber *)position forItem:(id<TGMediaEditableItem>)item;
- (UIImage *)coverImageForItem:(NSObject<TGMediaEditableItem> *)item;
- (NSNumber *)coverPositionForItem:(NSObject<TGMediaEditableItem> *)item;
- (void)setCoverImage:(UIImage *)image position:(NSNumber *)position forItem:(id<TGMediaEditableItem>)item;
- (void)setTemporaryRep:(id)rep forItem:(id<TGMediaEditableItem>)item;

View File

@ -64,6 +64,7 @@ class EmojiHeaderComponent: Component {
}
weak var animateFrom: UIView?
var sourceRect: CGRect?
weak var containerView: UIView?
let statusView: ComponentHostView<Empty>
@ -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
})

View File

@ -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()

View File

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

View File

@ -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([])
}

View File

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

View File

@ -568,6 +568,21 @@ private final class StarsContextImpl {
self._state = state
self._statePromise.set(.single(state))
}
var onUpdate: Signal<Void, NoError> {
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<Void, NoError> {
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: {

View File

@ -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)!

View File

@ -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))")

View File

@ -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()

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

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

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

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

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/TelegramCore",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/AccountContext",
"//submodules/ComponentFlow",
"//submodules/Components/MultilineTextComponent",

View File

@ -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<Empty>()
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<Empty>, 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)
}
}

View File

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

View File

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

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "msgstar.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -354,10 +354,11 @@ public final class AccountContextImpl: AccountContext {
let _ = currentAppConfiguration.swap(value)
})
let langCode = sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode
self.currentCountriesConfiguration = Atomic(value: CountriesConfiguration(countries: loadCountryCodes()))
if !temp {
let currentCountriesConfiguration = self.currentCountriesConfiguration
self.countriesConfigurationDisposable = (self.engine.localization.getCountriesList(accountManager: sharedContext.accountManager, langCode: nil)
self.countriesConfigurationDisposable = (self.engine.localization.getCountriesList(accountManager: sharedContext.accountManager, langCode: langCode)
|> deliverOnMainQueue).start(next: { value in
let _ = currentCountriesConfiguration.swap(CountriesConfiguration(countries: value))
})

View File

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

View File

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

View File

@ -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<PremiumSource, NoError> = self.emojiStatusFileAndPackTitle.get()
|> take(1)
|> mapToSignal { emojiStatusFileAndPack -> Signal<PremiumSource, NoError> 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)))