diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index dbdee4588f..515c1c7dd2 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1045,6 +1045,7 @@ public protocol SharedAccountContext: AnyObject { func makeStarsPurchaseScreen(context: AccountContext, starsContext: StarsContext, options: [StarsTopUpOption], peerId: EnginePeer.Id?, requiredStars: Int64?, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>) -> ViewController func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction) -> ViewController + func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt, id: String?, date: Int32) -> ViewController func makeDebugSettingsController(context: AccountContext?) -> ViewController? diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift index df4143f00b..3dec889a25 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift @@ -102,7 +102,17 @@ final class HashtagSearchControllerNode: ASDisplayNode { self.myController?.displayNode.isHidden = true self.globalController?.displayNode.isHidden = true - self.isSearching.set(self.currentController?.searching.get() ?? .single(false)) + let isSearching: Signal + if let currentController = self.currentController { + isSearching = .single(true) + |> then( + currentController.searching.get() + |> delay(0.5, queue: Queue.mainQueue()) + ) + } else { + isSearching = .single(false) + } + self.isSearching.set(isSearching) } else { self.myController?.displayNode.isHidden = false self.globalController?.displayNode.isHidden = true diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift index 6a48b375f0..e4204dfe3f 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift @@ -63,6 +63,8 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { super.init() + self.searchBar.autocapitalization = .none + if hasCurrentChat { self.addSubnode(self.searchBar) } diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index f710657cce..deb6724b72 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -949,6 +949,15 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } } + public var autocapitalization: UITextAutocapitalizationType { + get { + return self.textField.autocapitalizationType + } + set { + self.textField.autocapitalizationType = newValue + } + } + private var validLayout: (CGSize, CGFloat, CGFloat)? private let fieldStyle: SearchBarStyle @@ -962,7 +971,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.forceSeparator = forceSeparator self.cancelText = cancelText self.icon = icon - + self.backgroundNode = NavigationBackgroundNode(color: theme.background) self.backgroundNode.isUserInteractionEnabled = false self.backgroundNode.isHidden = !displayBackground diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 4c238de4fd..0b904e4d8f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -545,7 +545,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) } dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) } dict[1345295095] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) } - dict[-1776926890] = { return Api.MessageAction.parse_messageActionPaymentSent($0) } + dict[-1482950556] = { return Api.MessageAction.parse_messageActionPaymentSent($0) } dict[-1892568281] = { return Api.MessageAction.parse_messageActionPaymentSentMe($0) } dict[-2132731265] = { return Api.MessageAction.parse_messageActionPhoneCall($0) } dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) } diff --git a/submodules/TelegramApi/Sources/Api14.swift b/submodules/TelegramApi/Sources/Api14.swift index f9b2095a7c..8f50176db2 100644 --- a/submodules/TelegramApi/Sources/Api14.swift +++ b/submodules/TelegramApi/Sources/Api14.swift @@ -604,7 +604,7 @@ public extension Api { case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32) case messageActionHistoryClear case messageActionInviteToGroupCall(call: Api.InputGroupCall, users: [Int64]) - case messageActionPaymentSent(flags: Int32, currency: String, totalAmount: Int64, invoiceSlug: String?) + case messageActionPaymentSent(flags: Int32, currency: String, totalAmount: Int64, invoiceSlug: String?, charge: Api.PaymentCharge?) case messageActionPaymentSentMe(flags: Int32, currency: String, totalAmount: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, charge: Api.PaymentCharge) case messageActionPhoneCall(flags: Int32, callId: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?) case messageActionPinMessage @@ -816,14 +816,15 @@ public extension Api { serializeInt64(item, buffer: buffer, boxed: false) } break - case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug): + case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug, let charge): if boxed { - buffer.appendInt32(-1776926890) + buffer.appendInt32(-1482950556) } serializeInt32(flags, buffer: buffer, boxed: false) serializeString(currency, buffer: buffer, boxed: false) serializeInt64(totalAmount, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeString(invoiceSlug!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {charge!.serialize(buffer, true)} break case .messageActionPaymentSentMe(let flags, let currency, let totalAmount, let payload, let info, let shippingOptionId, let charge): if boxed { @@ -1017,8 +1018,8 @@ public extension Api { return ("messageActionHistoryClear", []) case .messageActionInviteToGroupCall(let call, let users): return ("messageActionInviteToGroupCall", [("call", call as Any), ("users", users as Any)]) - case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug): - return ("messageActionPaymentSent", [("flags", flags as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("invoiceSlug", invoiceSlug as Any)]) + case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug, let charge): + return ("messageActionPaymentSent", [("flags", flags as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("invoiceSlug", invoiceSlug as Any), ("charge", charge as Any)]) case .messageActionPaymentSentMe(let flags, let currency, let totalAmount, let payload, let info, let shippingOptionId, let charge): return ("messageActionPaymentSentMe", [("flags", flags as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("payload", payload as Any), ("info", info as Any), ("shippingOptionId", shippingOptionId as Any), ("charge", charge as Any)]) case .messageActionPhoneCall(let flags, let callId, let reason, let duration): @@ -1395,12 +1396,17 @@ public extension Api { _3 = reader.readInt64() var _4: String? if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: Api.PaymentCharge? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.PaymentCharge + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionPaymentSent(flags: _1!, currency: _2!, totalAmount: _3!, invoiceSlug: _4) + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.MessageAction.messageActionPaymentSent(flags: _1!, currency: _2!, totalAmount: _3!, invoiceSlug: _4, charge: _5) } else { return nil diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index f29a5c916a..00ab8cba68 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -40,10 +40,18 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe return TelegramMediaAction(action: .phoneCall(callId: callId, discardReason: discardReason, duration: duration, isVideo: isVideo)) case .messageActionEmpty: return nil - case let .messageActionPaymentSent(flags, currency, totalAmount, invoiceSlug): + case let .messageActionPaymentSent(flags, currency, totalAmount, invoiceSlug, charge): let isRecurringInit = (flags & (1 << 2)) != 0 let isRecurringUsed = (flags & (1 << 3)) != 0 - return TelegramMediaAction(action: .paymentSent(currency: currency, totalAmount: totalAmount, invoiceSlug: invoiceSlug, isRecurringInit: isRecurringInit, isRecurringUsed: isRecurringUsed)) + + let chargeId: String? + switch charge { + case let .paymentCharge(id, _): + chargeId = id + default: + chargeId = nil + } + return TelegramMediaAction(action: .paymentSent(currency: currency, totalAmount: totalAmount, invoiceSlug: invoiceSlug, isRecurringInit: isRecurringInit, isRecurringUsed: isRecurringUsed, chargeId: chargeId)) case .messageActionPaymentSentMe: return nil case .messageActionScreenshotTaken: diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 6dbb501727..0117377665 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -101,7 +101,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case messageAutoremoveTimeoutUpdated(period: Int32, autoSettingSource: PeerId?) case gameScore(gameId: Int64, score: Int32) case phoneCall(callId: Int64, discardReason: PhoneCallDiscardReason?, duration: Int32?, isVideo: Bool) - case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?, isRecurringInit: Bool, isRecurringUsed: Bool) + case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?, isRecurringInit: Bool, isRecurringUsed: Bool, chargeId: String?) case customText(text: String, entities: [MessageTextEntity], additionalAttributes: CustomTextAttributes?) case botDomainAccessGranted(domain: String) case botAppAccessGranted(appName: String?, type: BotSendMessageAccessGrantedType?) @@ -164,7 +164,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } self = .phoneCall(callId: decoder.decodeInt64ForKey("i", orElse: 0), discardReason: discardReason, duration: decoder.decodeInt32ForKey("d", orElse: 0), isVideo: decoder.decodeInt32ForKey("vc", orElse: 0) != 0) case 15: - self = .paymentSent(currency: decoder.decodeStringForKey("currency", orElse: ""), totalAmount: decoder.decodeInt64ForKey("ta", orElse: 0), invoiceSlug: decoder.decodeOptionalStringForKey("invoiceSlug"), isRecurringInit: decoder.decodeBoolForKey("isRecurringInit", orElse: false), isRecurringUsed: decoder.decodeBoolForKey("isRecurringUsed", orElse: false)) + self = .paymentSent(currency: decoder.decodeStringForKey("currency", orElse: ""), totalAmount: decoder.decodeInt64ForKey("ta", orElse: 0), invoiceSlug: decoder.decodeOptionalStringForKey("invoiceSlug"), isRecurringInit: decoder.decodeBoolForKey("isRecurringInit", orElse: false), isRecurringUsed: decoder.decodeBoolForKey("isRecurringUsed", orElse: false), chargeId: decoder.decodeOptionalStringForKey("chargeId")) case 16: self = .customText(text: decoder.decodeStringForKey("text", orElse: ""), entities: decoder.decodeObjectArrayWithDecoderForKey("ent"), additionalAttributes: nil) case 17: @@ -293,7 +293,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeInt32(13, forKey: "_rawValue") encoder.encodeInt64(gameId, forKey: "i") encoder.encodeInt32(score, forKey: "s") - case let .paymentSent(currency, totalAmount, invoiceSlug, isRecurringInit, isRecurringUsed): + case let .paymentSent(currency, totalAmount, invoiceSlug, isRecurringInit, isRecurringUsed, chargeId): encoder.encodeInt32(15, forKey: "_rawValue") encoder.encodeString(currency, forKey: "currency") encoder.encodeInt64(totalAmount, forKey: "ta") @@ -304,6 +304,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } encoder.encodeBool(isRecurringInit, forKey: "isRecurringInit") encoder.encodeBool(isRecurringUsed, forKey: "isRecurringUsed") + if let chargeId = chargeId { + encoder.encodeString(chargeId, forKey: "chargeId") + } else { + encoder.encodeNil(forKey: "chargeId") + } case let .phoneCall(callId, discardReason, duration, isVideo): encoder.encodeInt32(14, forKey: "_rawValue") encoder.encodeInt64(callId, forKey: "i") diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index c8a574059e..4d176fc80f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -563,7 +563,7 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa switch source { case let .slug(slug): for media in message.media { - if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _) = action.action, invoiceSlug == slug { + if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _, _) = action.action, invoiceSlug == slug { if case let .Id(id) = message.id { receiptMessageId = id } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 9e87fb6957..f25ea03474 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -366,7 +366,7 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot switch source { case let .slug(slug): for media in message.media { - if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _) = action.action, invoiceSlug == slug { + if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _, _) = action.action, invoiceSlug == slug { if case let .Id(id) = message.id { receiptMessageId = id } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index a279c40ad6..2168b042b5 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -496,7 +496,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, var argumentAttributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]) argumentAttributes[1] = MarkdownAttributeSet(font: titleBoldFont, textColor: primaryTextColor, additionalAttributes: [:]) attributedString = addAttributesToStringWithRanges(formatWithArgumentRanges(baseString, ranges, [authorName, gameTitle ?? ""]), body: bodyAttributes, argumentAttributes: argumentAttributes) - case let .paymentSent(currency, totalAmount, _, isRecurringInit, isRecurringUsed): + case let .paymentSent(currency, totalAmount, _, isRecurringInit, isRecurringUsed, _): var invoiceMessage: EngineMessage? for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute, let message = message.associatedMessages[attribute.messageId] { @@ -548,13 +548,11 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, range = (mutableString.string as NSString).range(of: "{amount}") if range.location != NSNotFound { if currency == "XTR" { - let amountAttributedString = NSMutableAttributedString(string: " > \(totalAmount)", font: titleBoldFont, textColor: primaryTextColor) - if let range = amountAttributedString.string.range(of: ">"), let starImage = generateScaledImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), size: CGSize(width: 16.0, height: 16.0), opaque: false)?.withRenderingMode(.alwaysTemplate) { - amountAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: amountAttributedString.string)) - amountAttributedString.addAttribute(.foregroundColor, value: primaryTextColor, range: NSRange(range, in: amountAttributedString.string)) + let amountAttributedString = NSMutableAttributedString(string: "#\(totalAmount)", font: titleBoldFont, textColor: primaryTextColor) + if let range = amountAttributedString.string.range(of: "#") { + amountAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars), range: NSRange(range, in: amountAttributedString.string)) amountAttributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: amountAttributedString.string)) } - mutableString.replaceCharacters(in: range, with: amountAttributedString) } else { mutableString.replaceCharacters(in: range, with: NSAttributedString(string: formatCurrencyAmount(totalAmount, currency: currency), font: titleBoldFont, textColor: primaryTextColor)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 8ee49224b3..b47a210f41 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -98,6 +98,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ var result: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] = [] var skipText = false var messageWithCaptionToAdd: (Message, ChatMessageEntryAttributes)? + var messageWithFactCheckToAdd: (Message, ChatMessageEntryAttributes)? var isUnsupportedMedia = false var isStoryWithText = false var isAction = false @@ -263,6 +264,10 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } } + if let attribute = message.factCheckAttribute, case .Loaded = attribute.content, messageWithFactCheckToAdd == nil { + messageWithFactCheckToAdd = (message, itemAttributes) + } + inner: for media in message.media { if let webpage = media as? TelegramMediaWebpage { if case let .Loaded(content) = webpage.content { @@ -294,14 +299,6 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ if isUnsupportedMedia { result.append((message, ChatMessageUnsupportedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) needReactions = false - } else { - for attribute in message.attributes { - if let attribute = attribute as? FactCheckMessageAttribute, case .Loaded = attribute.content { - result.append((message, ChatMessageFactCheckBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) - needReactions = false - break - } - } } } @@ -317,6 +314,11 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } } + if let (messageWithFactCheckToAdd, itemAttributes) = messageWithFactCheckToAdd, !hasSeparateCommentsButton { + result.append((messageWithFactCheckToAdd, ChatMessageFactCheckBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) + needReactions = false + } + if let additionalContent = item.additionalContent { switch additionalContent { case let .eventLogPreviousMessage(previousMessage): diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 39c7d4f6c0..c4c232a975 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -395,6 +395,9 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.updateTopicInfo(topicInfo: (id, info)) case let .nameColors(colors): self.updateNameColors(colors: colors) + case .stars: + self.updateStars() + self.updateTintColor() } } else if let file = file { self.updateFile(file: file, attemptSynchronousLoad: attemptSynchronousLoad) @@ -480,6 +483,8 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { if file.isCustomTemplateEmoji { customColor = self.dynamicColor } + } else if let emoji = self.arguments?.emoji, let custom = emoji.custom, case .stars = custom { + customColor = self.dynamicColor } if customColor != nil { @@ -574,6 +579,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.contents = image?.cgImage } + private func updateStars() { + self.contents = starImage?.cgImage + } + private func updateFile(file: TelegramMediaFile, attemptSynchronousLoad: Bool) { guard let arguments = self.arguments else { return @@ -833,3 +842,13 @@ public final class CustomEmojiContainerView: UIView { } } } + +private let starImage: UIImage? = { + generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + + if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: .white), let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 2.0, dy: 2.0), byTiling: false) + } + })?.withRenderingMode(.alwaysTemplate) +}() diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift index 67bbb7e785..dab6a47196 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift @@ -79,6 +79,8 @@ private final class StarsTransactionSheetContent: CombinedComponent { if case let .peer(peer) = transaction.peer { peerIds.append(peer.id) } + case let .receipt(receipt, _, _): + peerIds.append(receipt.botPaymentId) } self.disposable = (context.engine.data.get( @@ -159,18 +161,20 @@ private final class StarsTransactionSheetContent: CombinedComponent { let additionalText: String let buttonText: String - let transactionId: String + let count: Int64 + let transactionId: String? let date: Int32 let toPeer: EnginePeer? + let photo: TelegramMediaWebFile? let gloss = false switch subject { case let .transaction(transaction): switch transaction.peer { - case .peer: - titleText = "Product Title" + case let .peer(peer): + titleText = transaction.title ?? peer.compactDisplayTitle case .appStore: - titleText = "In-app Purchase" + titleText = "In-App Purchase" case .playMarket: titleText = "Play Market" case .premiumBot: @@ -181,14 +185,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { titleText = "Unsupported" } - if transaction.count < 0 { - descriptionText = "- \(transaction.count * -1) ⭐️" - } else { - descriptionText = "+ \(transaction.count) ⭐️" - } - additionalText = strings.Stars_Transaction_Terms - buttonText = strings.Common_OK - + count = transaction.count transactionId = transaction.id date = transaction.date if case let .peer(peer) = transaction.peer { @@ -196,6 +193,27 @@ private final class StarsTransactionSheetContent: CombinedComponent { } else { toPeer = nil } + photo = transaction.photo + case let .receipt(receipt, id, dateValue): + titleText = receipt.invoiceMedia.title + count = (receipt.invoice.prices.first?.amount ?? receipt.invoiceMedia.totalAmount) * -1 + transactionId = id + date = dateValue + if let peer = state.peerMap[receipt.botPaymentId] { + toPeer = peer + } else { + toPeer = nil + } + photo = receipt.invoiceMedia.photo + } + + additionalText = strings.Stars_Transaction_Terms + buttonText = strings.Common_OK + + if count < 0 { + descriptionText = "- \(count * -1) ⭐️" + } else { + descriptionText = "+ \(count) ⭐️" } let title = title.update( @@ -218,6 +236,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { context: component.context, theme: theme, peers: toPeer.flatMap { [$0] } ?? [], + photo: photo, isVisible: true, hasIdleAnimations: true, hasScaleAnimation: false, @@ -247,7 +266,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { if let toPeer { tableItems.append(.init( id: "to", - title: strings.Stars_Transaction_Date, + title: strings.Stars_Transaction_To, component: AnyComponent( Button( content: AnyComponent( @@ -270,21 +289,23 @@ private final class StarsTransactionSheetContent: CombinedComponent { )) } - tableItems.append(.init( - id: "transaction", - title: strings.Stars_Transaction_Id, - component: AnyComponent( - TransactionCellComponent( - textColor: tableTextColor, - accentColor: tableLinkColor, - transactionId: transactionId, - copy: { - component.copyTransactionId() - } - ) - ), - insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0) - )) + if let transactionId { + tableItems.append(.init( + id: "transaction", + title: strings.Stars_Transaction_Id, + component: AnyComponent( + TransactionCellComponent( + textColor: tableTextColor, + accentColor: tableLinkColor, + transactionId: transactionId, + copy: { + component.copyTransactionId() + } + ) + ), + insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0) + )) + } tableItems.append(.init( id: "date", @@ -522,6 +543,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent { public class StarsTransactionScreen: ViewControllerComponentContainer { public enum Subject: Equatable { case transaction(StarsContext.State.Transaction) + case receipt(receipt: BotPaymentReceipt, id: String?, date: Int32) } private let context: AccountContext diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 75f5ae1f65..9d60a365b7 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1041,8 +1041,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if canSetupAutoremoveTimeout { strongSelf.presentAutoremoveSetup() } - case .paymentSent: - strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + case let .paymentSent(currency, _, _, _, _, id): + if currency == "XTR" { + let _ = (context.engine.payments.requestBotPaymentReceipt(messageId: message.id) + |> deliverOnMainQueue).start(next: { [weak self] receipt in + guard let self else { + return + } + self.push(self.context.sharedContext.makeStarsReceiptScreen(context: self.context, receipt: receipt, id: id, date: message.timestamp)) + }) + } else { + strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } /*for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { //strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId)) @@ -2709,19 +2719,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - let peerSignal: Signal - guard let peerId = strongSelf.chatLocation.peerId else { - return - } - peerSignal = strongSelf.context.account.postbox.loadedPeerWithId(peerId) - |> map(Optional.init) - let _ = (peerSignal - |> deliverOnMainQueue).startStandalone(next: { peer in - if let strongSelf = self { - let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) - strongSelf.effectiveNavigationController?.pushViewController(searchController) - } - }) + strongSelf.openHashtag(hashtag, peerName: nil) } }), ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in @@ -2925,7 +2923,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let invoice = media as? TelegramMediaInvoice { strongSelf.chatDisplayNode.dismissInput() if let receiptMessageId = invoice.receiptMessageId { - strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: receiptMessageId), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + if invoice.currency == "XTR" { + let _ = (strongSelf.context.engine.payments.requestBotPaymentReceipt(messageId: message.id) + |> deliverOnMainQueue).start(next: { [weak self] receipt in + guard let strongSelf = self else { + return + } + strongSelf.push(strongSelf.context.sharedContext.makeStarsReceiptScreen(context: strongSelf.context, receipt: receipt, id: nil, date: 0)) + }) + } else { + strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: receiptMessageId), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } } else { let inputData = Promise() inputData.set(BotCheckoutController.InputData.fetch(context: strongSelf.context, source: .message(message.id)) @@ -9586,6 +9594,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.resolvePeerByNameDisposable?.set((resolveSignal |> deliverOnMainQueue).start(next: { [weak self] peer in if let strongSelf = self, !hashtag.isEmpty { +// var peer = peer +// if peer?.id.isReplies == true { +// peer = nil +// } let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) strongSelf.effectiveNavigationController?.pushViewController(searchController) } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index af4d8c683e..d7df67602f 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2816,7 +2816,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } self.skippedShowSearchResultsAsListAnimationOnce = true inlineSearchResultsView.layer.allowsGroupOpacity = true - self.contentContainerNode.view.insertSubview(inlineSearchResultsView, aboveSubview: self.historyNodeContainer.view) + if let inlineSearchResultsView = self.inlineSearchResults?.view { + self.contentContainerNode.view.insertSubview(inlineSearchResultsView, aboveSubview: inlineSearchResultsView) + } else { + self.contentContainerNode.view.insertSubview(inlineSearchResultsView, aboveSubview: self.historyNodeContainer.view) + } } inlineSearchResultsTransition.setFrame(view: inlineSearchResultsView, frame: CGRect(origin: CGPoint(), size: layout.size)) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index a317165c29..66bb2de724 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -480,6 +480,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState isEmbeddedMode = true } + if case let .customChatContents(customChatContents) = chatPresentationInterfaceState.subject, case .hashTagSearch = customChatContents.kind { + isEmbeddedMode = true + } + var hasExpandedAudioTranscription = false if let messageNode = messageNode as? ChatMessageBubbleItemNode { hasExpandedAudioTranscription = messageNode.hasExpandedAudioTranscription() @@ -1703,29 +1707,28 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState clearCacheAsDelete = true } - if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info, canEditFactCheck(appConfig: appConfig) { - var hasFactCheck = false - for attribute in message.attributes { - if let _ = attribute as? FactCheckMessageAttribute { - hasFactCheck = true - break - } + var canAddFactCheck = true + if message.media.contains(where: { $0 is TelegramMediaAction || $0 is TelegramMediaGiveaway }) { + canAddFactCheck = false } - let title: String - if hasFactCheck { - title = chatPresentationInterfaceState.strings.Conversation_ContextMenuEditFactCheck - } else { - title = chatPresentationInterfaceState.strings.Conversation_ContextMenuAddFactCheck + if canAddFactCheck { + let hasFactCheck = message.factCheckAttribute != nil + let title: String + if hasFactCheck { + title = chatPresentationInterfaceState.strings.Conversation_ContextMenuEditFactCheck + } else { + title = chatPresentationInterfaceState.strings.Conversation_ContextMenuAddFactCheck + } + actions.append(.action(ContextMenuActionItem(text: title, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/FactCheck"), color: theme.actionSheet.primaryTextColor) + }, action: { c, f in + c?.dismiss(completion: { + controllerInteraction.editMessageFactCheck(messages[0].id) + }) + }))) } - actions.append(.action(ContextMenuActionItem(text: title, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/FactCheck"), color: theme.actionSheet.primaryTextColor) - }, action: { c, f in - c?.dismiss(completion: { - controllerInteraction.editMessageFactCheck(messages[0].id) - }) - }))) } // if message.id.peerId.isGroupOrChannel { // //TODO:localize @@ -1985,12 +1988,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } if let message = messages.first, case let .customChatContents(customChatContents) = chatPresentationInterfaceState.subject { - actions.removeAll() - switch customChatContents.kind { case .hashTagSearch: break case .quickReplyMessageInput: + actions.removeAll() if !messageText.isEmpty || (resourceAvailable && isImage) || diceEmoji != nil { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor) @@ -2054,7 +2056,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }))) } case .businessLinkSetup: - break + actions.removeAll() } } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index eab12fb821..4b84fb4b37 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2634,6 +2634,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { public func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction) -> ViewController { return StarsTransactionScreen(context: context, subject: .transaction(transaction), action: {}) } + + public func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt, id: String?, date: Int32) -> ViewController { + return StarsTransactionScreen(context: context, subject: .receipt(receipt: receipt, id: id, date: date), action: {}) + } } private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? { diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index 77b5c6374c..f15e284ace 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -407,6 +407,7 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable { public enum Custom: Codable { case topic(id: Int64, info: EngineMessageHistoryThread.Info) case nameColors([UInt32]) + case stars } public let interactivelySelectedFromPackId: ItemCollectionId?