From 72c58813a878df5554bfda1d461f4680e28766dc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Aug 2025 23:25:24 +0200 Subject: [PATCH] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 + .../Sources/Node/ChatListNoticeItem.swift | 2 +- .../ChatSendMessageContextScreen.swift | 2 +- .../Sources/MediaPickerScreen.swift | 2 +- .../Sources/CreateGiveawayController.swift | 4 +- .../Sources/GiveawayInfoController.swift | 4 +- .../Sources/PremiumIntroScreen.swift | 281 +++++++++++------- .../Sources/ChannelStatsController.swift | 4 +- .../Sources/StarsTransactionItem.swift | 2 +- .../TelegramEngine/Payments/Stars.swift | 4 + .../Sources/ServiceMessageStrings.swift | 14 +- .../ChatMessageGiftBubbleContentNode.swift | 14 +- ...ChatMessageGiveawayBubbleContentNode.swift | 4 +- .../ChatMessagePaymentAlertController.swift | 8 +- .../Sources/ChatSendStarsScreen.swift | 2 +- .../Sources/GiftSetupScreen.swift | 4 +- .../Sources/GiftPurchaseAlertController.swift | 2 +- .../Sources/GiftTransferAlertController.swift | 2 +- .../Sources/GiftViewScreen.swift | 20 +- .../Sources/GiftWithdrawAlertController.swift | 2 +- .../Sources/MessagePriceItem.swift | 2 +- .../Sources/StarsAvatarComponent.swift | 204 +++++++------ .../Sources/StarsImageComponent.swift | 37 +++ .../Sources/StarsPurchaseScreen.swift | 2 +- .../Sources/StarsTransactionScreen.swift | 39 ++- .../Sources/StarsStatisticsScreen.swift | 2 +- .../StarsTransactionsListPanelComponent.swift | 8 +- .../Sources/StarsTransactionsScreen.swift | 4 +- .../Sources/StarsTransferScreen.swift | 18 +- .../Sources/StarsWithdrawalScreen.swift | 4 +- .../Chat/ChatControllerPaidMessage.swift | 2 +- submodules/TelegramUI/Sources/OpenUrl.swift | 5 + 32 files changed, 437 insertions(+), 273 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 5d3928e442..ad230f06e1 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14870,3 +14870,9 @@ Sorry for the inconvenience."; "Stories.Post.AlbumCount_any" = "%@ Albums"; "Gift.Options.Gift.Premium" = "premium"; + +"Stars.Transaction.FragmentTopUpTon.Title" = "TON Top-Up"; +"Stars.Transaction.FragmentWithdrawalTon.Title" = "TON Withdrawal"; + +"Stars.Transaction.SearchFee.Title" = "Extra Search Fee"; +"Stars.Intro.Transaction.SearchFee" = "Extra Search Fee"; diff --git a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift index d7373fa66d..eae620ae40 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift @@ -277,7 +277,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { case let .starsSubscriptionLowBalance(amount, peers): let title: String let text: String - let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(amount.value)) + let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(clamping: amount.value)) if let peer = peers.first, peers.count == 1 { title = item.strings.ChatList_SubscriptionsLowBalance_Single_Title(starsValue, peer.compactDisplayTitle).string text = item.strings.ChatList_SubscriptionsLowBalance_Single_Text diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift index dee2ad2dfa..b5f4e7606f 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift @@ -592,7 +592,7 @@ final class ChatSendMessageContextScreenComponent: Component { let titleLayout: ContextMenuActionItemTextLayout if let currentPrice { title = environment.strings.Attachment_Paid_EditPrice - titleLayout = .secondLineWithValue(environment.strings.Attachment_Paid_EditPrice_Stars(Int32(currentPrice))) + titleLayout = .secondLineWithValue(environment.strings.Attachment_Paid_EditPrice_Stars(Int32(clamping: currentPrice))) } else { title = environment.strings.Attachment_Paid_Create titleLayout = .twoLinesMax diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 70114ae942..ec7db2e9b7 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2799,7 +2799,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let titleLayout: ContextMenuActionItemTextLayout if let price { title = strings.Attachment_Paid_EditPrice - titleLayout = .secondLineWithValue(strings.Attachment_Paid_EditPrice_Stars(Int32(price))) + titleLayout = .secondLineWithValue(strings.Attachment_Paid_EditPrice_Stars(Int32(clamping: price))) } else { title = strings.Attachment_Paid_Create titleLayout = .twoLinesMax diff --git a/submodules/PremiumUI/Sources/CreateGiveawayController.swift b/submodules/PremiumUI/Sources/CreateGiveawayController.swift index b7ebc06d50..523309152b 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayController.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayController.swift @@ -793,7 +793,7 @@ private func createGiveawayControllerEntries( if !state.starsExpanded && product.giveawayOption.isExtended { continue } - let giftTitle: String = presentationData.strings.BoostGift_Stars_Stars(Int32(product.giveawayOption.count)) + let giftTitle: String = presentationData.strings.BoostGift_Stars_Stars(Int32(clamping: product.giveawayOption.count)) let maxWinners = product.giveawayOption.winners.sorted(by: { $0.users < $1.users }).last?.users ?? 1 let starsPerUser: Int64 @@ -951,7 +951,7 @@ private func createGiveawayControllerEntries( entries.append(.prizeDescriptionText(presentationData.theme, presentationData.strings.BoostGift_AdditionalPrizesPlaceholder, state.prizeDescription, state.subscriptions)) if state.mode == .starsGiveaway { - let starsString = presentationData.strings.BoostGift_AdditionalPrizesInfoStars(Int32(state.stars)) + let starsString = presentationData.strings.BoostGift_AdditionalPrizesInfoStars(Int32(clamping: state.stars)) if state.prizeDescription.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { prizeDescriptionInfoText = presentationData.strings.BoostGift_AdditionalPrizesInfoStarsOn(starsString, "").string } else { diff --git a/submodules/PremiumUI/Sources/GiveawayInfoController.swift b/submodules/PremiumUI/Sources/GiveawayInfoController.swift index 9893b76288..b50ada30e0 100644 --- a/submodules/PremiumUI/Sources/GiveawayInfoController.swift +++ b/submodules/PremiumUI/Sources/GiveawayInfoController.swift @@ -154,7 +154,7 @@ public func presentGiveawayInfoController( let intro: String if stars > 0 { - let starsString = presentationData.strings.Chat_Giveaway_Info_Stars_Stars(Int32(stars)) + let starsString = presentationData.strings.Chat_Giveaway_Info_Stars_Stars(Int32(clamping: stars)) if case .almostOver = status { if isGroup { intro = presentationData.strings.Chat_Giveaway_Info_Stars_Group_EndedIntro(peerName, starsString).string @@ -280,7 +280,7 @@ public func presentGiveawayInfoController( let intro: String if stars > 0 { - let starsString = presentationData.strings.Chat_Giveaway_Info_Stars_Stars(Int32(stars)) + let starsString = presentationData.strings.Chat_Giveaway_Info_Stars_Stars(Int32(clamping: stars)) if isGroup { intro = presentationData.strings.Chat_Giveaway_Info_Stars_Group_EndedIntro(peerName, starsString).string } else { diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index c8a4210d2b..3eece91a02 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -931,10 +931,10 @@ struct PremiumIntroConfiguration { private struct PremiumProduct: Equatable { let option: PremiumPromoConfiguration.PremiumProductOption - let storeProduct: InAppPurchaseManager.Product + let storeProduct: InAppPurchaseManager.Product? var id: String { - return self.option.storeProductId ?? self.storeProduct.id + return self.storeProduct?.id ?? self.option.botUrl } var months: Int32 { @@ -942,11 +942,39 @@ private struct PremiumProduct: Equatable { } var price: String { - return self.storeProduct.price + if let storeProduct = self.storeProduct { + return storeProduct.price + } else { + return formatCurrencyAmount(self.option.amount, currency: self.option.currency) + } } var pricePerMonth: String { - return self.storeProduct.pricePerMonth(Int(self.months)) + if let storeProduct = self.storeProduct { + return storeProduct.pricePerMonth(Int(self.months)) + } else { + return formatCurrencyAmount(self.option.amount / Int64(self.months), currency: self.option.currency) + } + } + + var priceCurrencyAndAmount: (currency: String, amount: Int64) { + if let priceCurrencyAndAmount = self.storeProduct?.priceCurrencyAndAmount { + return priceCurrencyAndAmount + } else { + return (self.option.currency, self.option.amount) + } + } + + var priceValue: NSDecimalNumber { + if let priceValue = self.storeProduct?.priceValue { + return priceValue + } else { + return self.optionPriceValue + } + } + + var optionPriceValue: NSDecimalNumber { + return currencyToFractionalAmount(value: self.option.amount, currency: self.option.currency).flatMap { NSDecimalNumber(floatLiteral: $0) } ?? 0.0 } var isCurrent: Bool { @@ -1552,7 +1580,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } var isBiannual: Bool { - return self.products?.first(where: { $0.id == self.selectedProductId })?.id.hasSuffix(".biannual") ?? false + return self.products?.first(where: { $0.id == self.selectedProductId })?.months == 24 } var canUpgrade: Bool { @@ -1779,7 +1807,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { state.selectedProductId = context.component.selectedProductId state.validPurchases = context.component.validPurchases state.isPremium = context.component.isPremium - + let theme = environment.theme let strings = environment.strings let presentationData = context.component.screenContext.presentationData @@ -1957,15 +1985,23 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { if let products = state.products, products.count > 1, state.isPremium == false || (!context.component.justBought && state.canUpgrade) { var optionsItems: [SectionGroupComponent.Item] = [] - let shortestOptionPrice: (Int64, NSDecimalNumber) + let shortestProductPrice: (Int64, NSDecimalNumber) if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) { - shortestOptionPrice = (Int64(Float(product.storeProduct.priceCurrencyAndAmount.amount)), product.storeProduct.priceValue) + shortestProductPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue) } else { - shortestOptionPrice = (1, NSDecimalNumber(decimal: 1)) + shortestProductPrice = (1, NSDecimalNumber(decimal: 1)) } let currentProductMonths = state.products?.first(where: { $0.isCurrent })?.months ?? 0 + var referenceProduct: InAppPurchaseManager.Product? + for product in products { + if let storeProduct = product.storeProduct { + referenceProduct = storeProduct + break + } + } + var i = 0 for product in products { let giftTitle: String @@ -1973,13 +2009,13 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { giftTitle = strings.Premium_Monthly } else if product.id.hasSuffix(".semiannual") { giftTitle = strings.Premium_Semiannual - } else if product.id.hasSuffix(".biannual") { + } else if product.months == 24 { giftTitle = strings.Premium_Biannual } else { giftTitle = strings.Premium_Annual } - let fraction = Float(product.storeProduct.priceCurrencyAndAmount.amount) / Float(product.months) / Float(shortestOptionPrice.0) + let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(product.months) / Float(shortestProductPrice.0) let discountValue = Int(round((1.0 - fraction) * 20.0) * 5.0) let discount: String if discountValue > 0 { @@ -1988,13 +2024,16 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { discount = "" } - let defaultPrice = product.storeProduct.defaultPrice(shortestOptionPrice.1, monthsCount: Int(product.months)) + var defaultPrice: String = "" + if let referenceProduct { + defaultPrice = referenceProduct.defaultPrice(shortestProductPrice.1, monthsCount: Int(product.months)) + } var subtitle = "" var accessibilitySubtitle = "" var pricePerMonth = product.price if product.months > 1 { - pricePerMonth = product.storeProduct.pricePerMonth(Int(product.months)) + pricePerMonth = product.pricePerMonth if discountValue > 0 { subtitle = "**\(defaultPrice)** \(product.price)" @@ -2964,6 +3003,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { private let source: PremiumSource private let updateInProgress: (Bool) -> Void private let present: (ViewController) -> Void + var navigationController: (() -> NavigationController?)? private let completion: () -> Void var topContentOffset: CGFloat? @@ -2987,7 +3027,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { var emojiPackTitle: String? private var emojiFileDisposable: Disposable? - private var disposable: Disposable? private var paymentDisposable = MetaDisposable() private var activationDisposable = MetaDisposable() @@ -3002,7 +3041,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { } var isBiannual: Bool { - return self.products?.first(where: { $0.id == self.selectedProductId })?.id.hasSuffix(".biannual") ?? false + return self.products?.first(where: { $0.id == self.selectedProductId })?.months == 24 } var canUpgrade: Bool { @@ -3017,7 +3056,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent { } } - init(screenContext: PremiumIntroScreen.ScreenContext, source: PremiumSource, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void) { + init( + screenContext: PremiumIntroScreen.ScreenContext, + source: PremiumSource, + forceHasPremium: Bool, + updateInProgress: @escaping (Bool) -> Void, + present: @escaping (ViewController) -> Void, + completion: @escaping () -> Void + ) { self.screenContext = screenContext self.source = source self.updateInProgress = updateInProgress @@ -3093,6 +3139,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { for option in promoConfiguration.premiumProductOptions { if let product = availableProducts.first(where: { $0.id == option.storeProductId }), product.isSubscription { products.append(PremiumProduct(option: option, storeProduct: product)) + } else { + products.append(PremiumProduct(option: option, storeProduct: nil)) } } @@ -3219,117 +3267,127 @@ private final class PremiumIntroScreenComponent: CombinedComponent { self.updateInProgress(true) self.updated(transition: .immediate) - let purpose: AppStoreTransactionPurpose = isUpgrade ? .upgrade : .subscription - - let canPurchasePremium: Signal - switch self.screenContext { - case let .accountContext(context): - canPurchasePremium = context.engine.payments.canPurchasePremium(purpose: purpose) - case let .sharedContext(_, engine, _): - canPurchasePremium = engine.payments.canPurchasePremium(purpose: purpose) - } - let _ = (canPurchasePremium - |> deliverOnMainQueue).start(next: { [weak self] available in - guard let self else { - return + if let storeProduct = premiumProduct.storeProduct { + let purpose: AppStoreTransactionPurpose = isUpgrade ? .upgrade : .subscription + + let canPurchasePremium: Signal + switch self.screenContext { + case let .accountContext(context): + canPurchasePremium = context.engine.payments.canPurchasePremium(purpose: purpose) + case let .sharedContext(_, engine, _): + canPurchasePremium = engine.payments.canPurchasePremium(purpose: purpose) } - if available { - self.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct.storeProduct, purpose: purpose) - |> deliverOnMainQueue).start(next: { [weak self] status in - if let self, case .purchased = status { - let activation: Signal - if let context = self.screenContext.context { - activation = context.account.postbox.peerView(id: context.account.peerId) - |> castError(AssignAppStoreTransactionError.self) - |> take(until: { view in - if let peer = view.peers[view.peerId], peer.isPremium { - return SignalTakeAction(passthrough: false, complete: true) - } else { - return SignalTakeAction(passthrough: false, complete: false) + let _ = (canPurchasePremium + |> deliverOnMainQueue).start(next: { [weak self] available in + guard let self else { + return + } + if available { + self.paymentDisposable.set((inAppPurchaseManager.buyProduct(storeProduct, purpose: purpose) + |> deliverOnMainQueue).start(next: { [weak self] status in + if let self, case .purchased = status { + let activation: Signal + if let context = self.screenContext.context { + activation = context.account.postbox.peerView(id: context.account.peerId) + |> castError(AssignAppStoreTransactionError.self) + |> take(until: { view in + if let peer = view.peers[view.peerId], peer.isPremium { + return SignalTakeAction(passthrough: false, complete: true) + } else { + return SignalTakeAction(passthrough: false, complete: false) + } + }) + |> mapToSignal { _ -> Signal in + return .never() } - }) - |> mapToSignal { _ -> Signal in - return .never() + |> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout)) + } else { + activation = .complete() } - |> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout)) - } else { - activation = .complete() - } - - self.activationDisposable.set((activation - |> deliverOnMainQueue).start(error: { [weak self] _ in - if let self { + + self.activationDisposable.set((activation + |> deliverOnMainQueue).start(error: { [weak self] _ in + if let self { + self.inProgress = false + self.updateInProgress(false) + + self.updated(transition: .immediate) + + if let context = self.screenContext.context { + addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail") + } + + let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + self.present(alertController) + } + }, completed: { [weak self] in + guard let self else { + return + } + if let context = self.screenContext.context { + let _ = updatePremiumPromoConfigurationOnce(account: context.account).start() + } self.inProgress = false self.updateInProgress(false) - self.updated(transition: .immediate) + self.isPremium = true + self.justBought = true - if let context = self.screenContext.context { - addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail") - } - - let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown - let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - self.present(alertController) - } - }, completed: { [weak self] in - guard let self else { - return - } + self.updated(transition: .easeInOut(duration: 0.25)) + self.completion() + })) + } + }, error: { [weak self] error in + guard let self else { + return + } + self.inProgress = false + self.updateInProgress(false) + self.updated(transition: .immediate) + + var errorText: String? + switch error { + case .generic: + errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + case .network: + errorText = presentationData.strings.Premium_Purchase_ErrorNetwork + case .notAllowed: + errorText = presentationData.strings.Premium_Purchase_ErrorNotAllowed + case .cantMakePayments: + errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments + case .assignFailed: + errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + case .tryLater: + errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + case .cancelled: + break + } + + if let errorText = errorText { if let context = self.screenContext.context { - let _ = updatePremiumPromoConfigurationOnce(account: context.account).start() + addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail") } - self.inProgress = false - self.updateInProgress(false) - self.isPremium = true - self.justBought = true - - self.updated(transition: .easeInOut(duration: 0.25)) - self.completion() - })) - } - }, error: { [weak self] error in - guard let self else { - return - } + let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + self.present(alertController) + } + })) + } else { self.inProgress = false self.updateInProgress(false) self.updated(transition: .immediate) - - var errorText: String? - switch error { - case .generic: - errorText = presentationData.strings.Premium_Purchase_ErrorUnknown - case .network: - errorText = presentationData.strings.Premium_Purchase_ErrorNetwork - case .notAllowed: - errorText = presentationData.strings.Premium_Purchase_ErrorNotAllowed - case .cantMakePayments: - errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments - case .assignFailed: - errorText = presentationData.strings.Premium_Purchase_ErrorUnknown - case .tryLater: - errorText = presentationData.strings.Premium_Purchase_ErrorUnknown - case .cancelled: - break - } - - if let errorText = errorText { - if let context = self.screenContext.context { - addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail") - } - - let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - self.present(alertController) - } - })) - } else { + } + }) + } else if case let .accountContext(context) = self.screenContext, let navigationController = self.navigationController?() { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: premiumProduct.option.botUrl, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + + Queue.mainQueue().after(3.0) { self.inProgress = false self.updateInProgress(false) self.updated(transition: .immediate) } - }) + } } func updateIsFocused(_ isFocused: Bool) { @@ -3366,6 +3424,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent { return { context in let environment = context.environment[EnvironmentType.self].value let state = context.state + state.navigationController = { [weak environment] in + return environment?.controller()?.navigationController as? NavigationController + } let background = background.update(component: Rectangle(color: environment.theme.list.blocksBackgroundColor), environment: {}, availableSize: context.availableSize, transition: context.transition) diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index dd808637c0..3602d25336 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1019,7 +1019,7 @@ private enum StatsEntry: ItemListNodeEntry { icon = .image(color: color, name: "Premium/Unclaimed") } else if boost.flags.contains(.isGiveaway) { if let stars = boost.stars { - title = presentationData.strings.Stats_Boosts_Stars(Int32(stars)) + title = presentationData.strings.Stats_Boosts_Stars(Int32(clamping: stars)) icon = .image(color: .stars, name: "Premium/PremiumStar") expiresString = expiresValue } else { @@ -1476,7 +1476,7 @@ private func boostsEntries( title = presentationData.strings.Stats_Boosts_PrepaidGiveawayCount(giveaway.quantity) text = presentationData.strings.Stats_Boosts_PrepaidGiveawayMonths("\(months)").string case let .stars(stars, _): - title = presentationData.strings.Stats_Boosts_Stars(Int32(stars)) + title = presentationData.strings.Stats_Boosts_Stars(Int32(clamping: stars)) text = presentationData.strings.Stats_Boosts_StarsWinners(giveaway.quantity) } entries.append(.boostPrepaid(i, presentationData.theme, title, text, giveaway)) diff --git a/submodules/StatisticsUI/Sources/StarsTransactionItem.swift b/submodules/StatisticsUI/Sources/StarsTransactionItem.swift index 13d4e41574..34355bb396 100644 --- a/submodules/StatisticsUI/Sources/StarsTransactionItem.swift +++ b/submodules/StatisticsUI/Sources/StarsTransactionItem.swift @@ -347,7 +347,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode { theme: item.presentationData.theme, title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)), contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0), - leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: item.context, theme: item.presentationData.theme, peer: item.transaction.peer, photo: nil, media: [], uniqueGift: nil, backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor))), false), + leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: item.context, theme: item.presentationData.theme, peer: .transactionPeer(item.transaction.peer), photo: nil, media: [], uniqueGift: nil, backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor))), false), icon: nil, accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel, iconName: itemIconName, iconColor: itemIconColor))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))), action: { [weak self] _ in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index c52058a60e..0cca0de749 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -720,6 +720,9 @@ private extension StarsContext.State.Transaction { if (apiFlags & (1 << 22)) != 0 { flags.insert(.isStarGiftResale) } + if (apiFlags & (1 << 24)) != 0 { + flags.insert(.isPostsSearch) + } let media = extendedMedia.flatMap({ $0.compactMap { textMediaAndExpirationTimerFromApiMedia($0, PeerId(0)).media } }) ?? [] let _ = subscriptionPeriod @@ -774,6 +777,7 @@ public final class StarsContext { public static let isPaidMessage = Flags(rawValue: 1 << 7) public static let isBusinessTransfer = Flags(rawValue: 1 << 8) public static let isStarGiftResale = Flags(rawValue: 1 << 9) + public static let isPostsSearch = Flags(rawValue: 1 << 10) } public enum Peer: Equatable { diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 432bf5be1f..c5a341fc21 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -96,7 +96,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, for attribute in message.attributes { if let attribute = attribute as? PaidStarsMessageAttribute { let messageCount = Int32(messageCount ?? 1) - let price = strings.Notification_PaidMessage_Stars(Int32(attribute.stars.value) * messageCount) + let price = strings.Notification_PaidMessage_Stars(Int32(clamping: attribute.stars.value) * messageCount) if message.author?.id == accountPeerId { if messageCount > 1 { let messagesString = strings.Notification_PaidMessage_Messages(messageCount) @@ -1048,7 +1048,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } let resultTitleString: PresentationStrings.FormattedString if let stars { - let starsString = strings.Notification_StarsGiveawayStarted_Stars(Int32(stars)) + let starsString = strings.Notification_StarsGiveawayStarted_Stars(Int32(clamping: stars)) resultTitleString = isGroup ? strings.Notification_StarsGiveawayStartedGroup(compactAuthorName, starsString) : strings.Notification_StarsGiveawayStarted(compactAuthorName, starsString) } else { resultTitleString = isGroup ? strings.Notification_GiveawayStartedGroup(compactAuthorName) : strings.Notification_GiveawayStarted(compactAuthorName) @@ -1146,7 +1146,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, if let upgradeStars { finalPrice += upgradeStars } - let starsPrice = strings.Notification_StarsGift_Stars(Int32(finalPrice)) + let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: finalPrice)) var authorName = compactAuthorName var peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)] if message.id.peerId.namespace == Namespaces.Peer.CloudUser && message.id.peerId.id._internalGetInt64Value() == 777000 { @@ -1207,7 +1207,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let starsString: String switch resaleStars.currency { case .stars: - starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value)) + starsString = strings.Notification_StarsGift_Bought_Stars(Int32(clamping: resaleStars.amount.value)) case .ton: starsString = formatTonAmountText(resaleStars.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } @@ -1246,7 +1246,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let starsString: String switch resaleStars.currency { case .stars: - starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value)) + starsString = strings.Notification_StarsGift_Bought_Stars(Int32(clamping: resaleStars.amount.value)) case .ton: starsString = formatTonAmountText(resaleStars.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } @@ -1262,7 +1262,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } } case let .paidMessagesRefunded(_, stars): - let starsString = strings.Notification_PaidMessageRefund_Stars(Int32(stars)) + let starsString = strings.Notification_PaidMessageRefund_Stars(Int32(clamping: stars)) var isOutgoing = false var messagePeer: EnginePeer? @@ -1306,7 +1306,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = addAttributesToStringWithRanges(resultString._tuple, body: bodyAttributes, argumentAttributes: attributes) } case let .paidMessagesPriceEdited(stars, broadcastMessagesAllowed): - let starsString = strings.Notification_PaidMessagePriceChanged_Stars(Int32(stars)) + let starsString = strings.Notification_PaidMessagePriceChanged_Stars(Int32(clamping: stars)) if message.author?.id == accountPeerId { let resultString: PresentationStrings.FormattedString resultString = strings.Notification_PaidMessagePriceChangedYou(starsString) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 435ca251e4..6e072810c9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -503,7 +503,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { peerName = EnginePeer(channel).compactDisplayTitle } title = item.presentationData.strings.Notification_StarsGiveaway_Title - let starsString = item.presentationData.strings.Notification_StarsGiveaway_Subtitle_Stars(Int32(count)).replacingOccurrences(of: " ", with: "\u{00A0}") + let starsString = item.presentationData.strings.Notification_StarsGiveaway_Subtitle_Stars(Int32(clamping: count)).replacingOccurrences(of: " ", with: "\u{00A0}") text = item.presentationData.strings.Notification_StarsGiveaway_Subtitle(peerName, starsString).string case let .giftCode(_, fromGiveaway, unclaimed, channelId, monthsValue, _, _, _, _, giftText, giftEntities): if channelId == nil { @@ -576,23 +576,23 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { text = item.presentationData.strings.Notification_StarGift_Subtitle_Upgraded } else if incoming { if converted { - text = item.presentationData.strings.Notification_StarGift_Subtitle_Converted(item.presentationData.strings.Notification_StarGift_Subtitle_Converted_Stars(Int32(convertStars ?? 0))).string + text = item.presentationData.strings.Notification_StarGift_Subtitle_Converted(item.presentationData.strings.Notification_StarGift_Subtitle_Converted_Stars(Int32(clamping: convertStars ?? 0))).string } else if upgradeStars != nil { text = item.presentationData.strings.Notification_StarGift_Subtitle_Upgrade } else if isSelfGift && canUpgrade { text = item.presentationData.strings.Notification_StarsGift_Subtitle_Self } else if savedToProfile { if let convertStars { - text = item.presentationData.strings.Notification_StarGift_Subtitle_Displaying(item.presentationData.strings.Notification_StarGift_Subtitle_Displaying_Stars(Int32(convertStars))).string + text = item.presentationData.strings.Notification_StarGift_Subtitle_Displaying(item.presentationData.strings.Notification_StarGift_Subtitle_Displaying_Stars(Int32(clamping: convertStars))).string } else { text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle_Displaying } } else { if let convertStars, convertStars > 0 { if isChannelGift { - text = item.presentationData.strings.Notification_StarGift_Subtitle_Channel(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string + text = item.presentationData.strings.Notification_StarGift_Subtitle_Channel(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(clamping: convertStars))).string } else { - text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string + text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(clamping: convertStars))).string } } else { text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle @@ -605,7 +605,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { } if peerName.isEmpty { if let convertStars, convertStars > 0 { - let starsString = item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars)).replacingOccurrences(of: " ", with: "\u{00A0}") + let starsString = item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(clamping: convertStars)).replacingOccurrences(of: " ", with: "\u{00A0}") text = item.presentationData.strings.Notification_StarGift_Subtitle(starsString).string } else { text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle @@ -614,7 +614,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { if upgradeStars != nil { text = item.presentationData.strings.Notification_StarGift_Subtitle_Upgrade_Other(peerName).string } else if let convertStars, convertStars > 0 { - let starsString = item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars)).replacingOccurrences(of: " ", with: "\u{00A0}") + let starsString = item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(clamping: convertStars)).replacingOccurrences(of: " ", with: "\u{00A0}") let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, starsString) text = formattedString.string if let starsRange = formattedString.ranges.last { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 3e9be886b1..dc611f355c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -350,7 +350,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, } ), textAlignment: .center) case let .stars(amount): - let starsString = item.presentationData.strings.Chat_Giveaway_Message_Stars_Stars(Int32(amount)) + let starsString = item.presentationData.strings.Chat_Giveaway_Message_Stars_Stars(Int32(clamping: amount)) prizeTextString = parseMarkdownIntoAttributedString(item.presentationData.strings.Chat_Giveaway_Message_Stars_PrizeText( starsString, item.presentationData.strings.Chat_Giveaway_Message_Stars_Winners(giveaway.quantity) @@ -468,7 +468,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, dateTextString = NSAttributedString(string: stringForFullDate(timestamp: giveaway.untilDate, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat), font: textFont, textColor: textColor) } else if let giveawayResults { if case let .stars(stars) = giveawayResults.prize { - let starsString = item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_Stars(Int32(stars)) + let starsString = item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_Stars(Int32(clamping: stars)) dateTextString = parseMarkdownIntoAttributedString(giveawayResults.winnersCount > 1 ? item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_Stars_Many(starsString).string : item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_Stars_One(starsString).string, attributes: MarkdownAttributes( body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift index ea8efafcfa..81fe4dc6bc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift @@ -505,8 +505,8 @@ public func chatMessagePaymentAlertController( let text: String if peers.count == 1, let peer = peers.first { - let amountString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(amount.value)) - let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(amount.value * Int64(count))) + let amountString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value)) + let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) if case let .channel(channel) = peer.chatOrMonoforumMainPeer, case .broadcast = channel.info { text = presentationData.strings.Chat_PaidMessage_Confirm_SingleComment_Text(EnginePeer(channel).compactDisplayTitle, amountString, totalString, messagesString).string } else { @@ -515,7 +515,7 @@ public func chatMessagePaymentAlertController( } else { let amount = totalAmount ?? amount let usersString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Users(Int32(peers.count)) - let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(amount.value * Int64(count))) + let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) text = presentationData.strings.Chat_PaidMessage_Confirm_Multiple_Text(usersString, totalString, messagesString).string } @@ -573,7 +573,7 @@ public func chatMessageRemovePaymentAlertController( text = strings.Chat_PaidMessage_RemoveFee_Text(peer.compactDisplayTitle).string } - let optionText = amount.flatMap { strings.Chat_PaidMessage_RemoveFee_Refund(strings.Chat_PaidMessage_RemoveFee_Refund_Stars(Int32($0.value))).string } + let optionText = amount.flatMap { strings.Chat_PaidMessage_RemoveFee_Refund(strings.Chat_PaidMessage_RemoveFee_Refund_Stars(Int32(clamping: $0.value))).string } let contentNode = ChatMessagePaymentAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, optionText: optionText, actions: actions, alignment: .horizontal) diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 308bc6913e..efb36b911a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -1866,7 +1866,7 @@ private final class ChatSendStarsScreenComponent: Component { switch component.initialData.subjectInitialData { case let .react(reactData): if let currentSentAmount = reactData.currentSentAmount { - text = environment.strings.SendStarReactions_TextSentStars(Int32(currentSentAmount)) + text = environment.strings.SendStarReactions_TextSentStars(Int32(clamping: currentSentAmount)) } else { text = environment.strings.SendStarReactions_TextGeneric(reactData.peer.debugDisplayTitle).string } diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index fd33e2dc21..e976012283 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -236,7 +236,7 @@ final class GiftSetupScreenComponent: Component { title: environment.strings.Gift_Send_Premium_Confirmation_Title, text: environment.strings.Gift_Send_Premium_Confirmation_Text( peer.compactDisplayTitle, - environment.strings.Gift_Send_Premium_Confirmation_Text_Stars(Int32(starsPrice)) + environment.strings.Gift_Send_Premium_Confirmation_Text_Stars(Int32(clamping: starsPrice)) ).string, actions: [ TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}), @@ -427,7 +427,7 @@ final class GiftSetupScreenComponent: Component { file: starGift.file, loop: true, title: nil, - text: presentationData.strings.Gift_Send_Success(self.peerMap[peerId]?.compactDisplayTitle ?? "", presentationData.strings.Gift_Send_Success_Stars(Int32(starGift.price))).string, + text: presentationData.strings.Gift_Send_Success(self.peerMap[peerId]?.compactDisplayTitle ?? "", presentationData.strings.Gift_Send_Success_Stars(Int32(clamping: starGift.price))).string, undoText: nil, customAction: nil ), diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift index 04c429990f..aea26bab1a 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift @@ -310,7 +310,7 @@ private final class GiftPurchaseAlertContentNode: AlertContentNode { if let resellPrice { switch resellPrice.currency { case .stars: - priceString = self.strings.Gift_Buy_Confirm_Text_Stars(Int32(resellPrice.amount.value)) + priceString = self.strings.Gift_Buy_Confirm_Text_Stars(Int32(clamping: resellPrice.amount.value)) case .ton: priceString = "**\(formatTonAmountText(resellPrice.amount.value, dateTimeFormat: presentationData.dateTimeFormat)) TON**" } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift index f18c8b6dda..63e4708941 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift @@ -297,7 +297,7 @@ public func giftTransferAlertController( let text: String let buttonText: String if transferStars > 0 { - text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(transferStars))).string + text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(clamping: transferStars))).string buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) $ \(transferStars)" } else { text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index ab29839a39..121b2f7bf7 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -548,7 +548,7 @@ private final class GiftViewSheetContent: CombinedComponent { let text = presentationData.strings.Gift_Convert_Period_Text( fromPeerName, - presentationData.strings.Gift_Convert_Period_Stars(Int32(convertStars)), + presentationData.strings.Gift_Convert_Period_Stars(Int32(clamping: convertStars)), presentationData.strings.Gift_Convert_Period_Days(days) ).string @@ -579,11 +579,11 @@ private final class GiftViewSheetContent: CombinedComponent { let text: String if isChannelGift { text = presentationData.strings.Gift_Convert_Success_ChannelText( - presentationData.strings.Gift_Convert_Success_ChannelText_Stars(Int32(convertStars)) + presentationData.strings.Gift_Convert_Success_ChannelText_Stars(Int32(clamping: convertStars)) ).string } else { text = presentationData.strings.Gift_Convert_Success_Text( - presentationData.strings.Gift_Convert_Success_Text_Stars(Int32(convertStars)) + presentationData.strings.Gift_Convert_Success_Text_Stars(Int32(clamping: convertStars)) ).string if let starsContext = self.context.starsContext { navigationController.pushViewController( @@ -932,7 +932,7 @@ private final class GiftViewSheetContent: CombinedComponent { let priceString: String switch price.currency { case .stars: - priceString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(price.amount.value)) + priceString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(clamping: price.amount.value)) case .ton: priceString = formatTonAmountText(price.amount.value, dateTimeFormat: presentationData.dateTimeFormat, maxDecimalPositions: nil) + " TON" } @@ -1257,7 +1257,7 @@ private final class GiftViewSheetContent: CombinedComponent { let originalPriceString: String switch resellAmount.currency { case .stars: - originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellAmount.amount.value)) + originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(clamping: resellAmount.amount.value)) case .ton: originalPriceString = formatTonAmountText(resellAmount.amount.value, dateTimeFormat: presentationData.dateTimeFormat, maxDecimalPositions: nil) + " TON" } @@ -1266,7 +1266,7 @@ private final class GiftViewSheetContent: CombinedComponent { let buttonText: String switch newPrice.currency { case .stars: - newPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(newPrice.amount.value)) + newPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(clamping: newPrice.amount.value)) buttonText = presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(newPrice.amount.value)) case .ton: let tonValueString = formatTonAmountText(newPrice.amount.value, dateTimeFormat: presentationData.dateTimeFormat, maxDecimalPositions: nil) @@ -2246,10 +2246,10 @@ private final class GiftViewSheetContent: CombinedComponent { descriptionText = strings.Gift_View_UpgradeDescription } } else { - descriptionText = isChannelGift ? strings.Gift_View_KeepOrConvertDescription_Channel(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string : strings.Gift_View_KeepOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string + descriptionText = isChannelGift ? strings.Gift_View_KeepOrConvertDescription_Channel(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(clamping: convertStars))).string : strings.Gift_View_KeepOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(clamping: convertStars))).string } } else { - descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(convertStars))).string + descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(clamping: convertStars))).string } } else { descriptionText = strings.Gift_View_BotDescription @@ -2258,7 +2258,7 @@ private final class GiftViewSheetContent: CombinedComponent { if let _ = upgradeStars { descriptionText = strings.Gift_View_FreeUpgradeOtherDescription(peer.compactDisplayTitle).string } else if case .message = subject, let convertStars { - descriptionText = strings.Gift_View_OtherDescription(peer.compactDisplayTitle, strings.Gift_View_OtherDescription_Stars(Int32(convertStars))).string + descriptionText = strings.Gift_View_OtherDescription(peer.compactDisplayTitle, strings.Gift_View_OtherDescription_Stars(Int32(clamping: convertStars))).string } else { descriptionText = "" } @@ -3085,7 +3085,7 @@ private final class GiftViewSheetContent: CombinedComponent { component: AnyComponent(Button( content: AnyComponent(ButtonContentComponent( context: component.context, - text: strings.Gift_View_Sale(strings.Gift_View_Sale_Stars(Int32(convertStars))).string, + text: strings.Gift_View_Sale(strings.Gift_View_Sale_Stars(Int32(clamping: convertStars))).string, color: theme.list.itemAccentColor )), action: { [weak state] in diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift index 9e00be92f4..7efd9dc945 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift @@ -174,7 +174,7 @@ private final class GiftWithdrawAlertContentNode: AlertContentNode { StarsAvatarComponent( context: self.context, theme: self.presentationTheme, - peer: .fragment, + peer: .transactionPeer(.fragment), photo: nil, media: [], uniqueGift: nil, diff --git a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift index a51864febf..72c25742c8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift @@ -368,7 +368,7 @@ private class MessagePriceItemNode: ListViewItemNode { strongSelf.leftTextNode.attributedText = NSAttributedString(string: "\(item.minValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) - let centralLeftText = item.value == 0 ? item.strings.Stars_SendMessage_PriceFree : item.strings.Privacy_Messages_Stars(Int32(item.value)) + let centralLeftText = item.value == 0 ? item.strings.Stars_SendMessage_PriceFree : item.strings.Privacy_Messages_Stars(Int32(clamping: item.value)) strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor) strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor.withMultipliedAlpha(0.5) : item.theme.list.itemSecondaryTextColor) diff --git a/submodules/TelegramUI/Components/Stars/StarsAvatarComponent/Sources/StarsAvatarComponent.swift b/submodules/TelegramUI/Components/Stars/StarsAvatarComponent/Sources/StarsAvatarComponent.swift index 0d53714a56..7a6c11acdd 100644 --- a/submodules/TelegramUI/Components/Stars/StarsAvatarComponent/Sources/StarsAvatarComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsAvatarComponent/Sources/StarsAvatarComponent.swift @@ -14,9 +14,14 @@ import MultilineTextComponent import GiftItemComponent public final class StarsAvatarComponent: Component { + public enum Peer: Equatable { + case transactionPeer(StarsContext.State.Transaction.Peer) + case search + } + let context: AccountContext let theme: PresentationTheme - let peer: StarsContext.State.Transaction.Peer? + let peer: StarsAvatarComponent.Peer? let photo: TelegramMediaWebFile? let media: [Media] let uniqueGift: StarGift.UniqueGift? @@ -26,7 +31,7 @@ public final class StarsAvatarComponent: Component { public init( context: AccountContext, theme: PresentationTheme, - peer: StarsContext.State.Transaction.Peer?, + peer: StarsAvatarComponent.Peer?, photo: TelegramMediaWebFile?, media: [Media], uniqueGift: StarGift.UniqueGift?, @@ -250,108 +255,125 @@ public final class StarsAvatarComponent: Component { switch component.peer { case .none: break - case let .peer(peer): - if !didSetup { - self.avatarNode.setPeer( - context: component.context, - theme: component.theme, - peer: peer, - synchronousLoad: true + case let .transactionPeer(peer): + switch peer { + case let .peer(peer): + if !didSetup { + self.avatarNode.setPeer( + context: component.context, + theme: component.theme, + peer: peer, + synchronousLoad: true + ) + self.backgroundView.isHidden = true + self.iconView.isHidden = true + self.avatarNode.isHidden = false + } + case .appStore: + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0x2a9ef1).cgColor, + UIColor(rgb: 0x72d5fd).cgColor + ], + direction: .mirroredDiagonal ) - self.backgroundView.isHidden = true - self.iconView.isHidden = true - self.avatarNode.isHidden = false + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Apple") + case .playMarket: + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0x54cb68).cgColor, + UIColor(rgb: 0xa0de7e).cgColor + ], + direction: .mirroredDiagonal + ) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Google") + case .fragment: + self.backgroundView.image = generateFilledCircleImage(diameter: size.width, color: UIColor(rgb: 0x1b1f24)) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment") + iconOffset = 2.0 + case .ads: + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0xffa85c).cgColor, + UIColor(rgb: 0xffcd6a).cgColor + ], + direction: .mirroredDiagonal + ) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white) + case .premiumBot: + iconInset = 7.0 + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0x6b93ff).cgColor, + UIColor(rgb: 0x6b93ff).cgColor, + UIColor(rgb: 0x8d77ff).cgColor, + UIColor(rgb: 0xb56eec).cgColor, + UIColor(rgb: 0xb56eec).cgColor + ], + direction: .mirroredDiagonal + ) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white) + case .apiLimitExtension: + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0x32b83b).cgColor, + UIColor(rgb: 0x87d93b).cgColor + ], + direction: .vertical + ) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = UIImage(bundleImageName: "Premium/Stars/PaidBroadcast") + case .unsupported: + iconInset = 7.0 + self.backgroundView.image = generateGradientFilledCircleImage( + diameter: size.width, + colors: [ + UIColor(rgb: 0xb1b1b1).cgColor, + UIColor(rgb: 0xcdcdcd).cgColor + ], + direction: .mirroredDiagonal + ) + self.backgroundView.isHidden = false + self.iconView.isHidden = false + self.avatarNode.isHidden = true + self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white) } - case .appStore: + case .search: + iconInset = 6.0 self.backgroundView.image = generateGradientFilledCircleImage( diameter: size.width, colors: [ UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor ], - direction: .mirroredDiagonal - ) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Apple") - case .playMarket: - self.backgroundView.image = generateGradientFilledCircleImage( - diameter: size.width, - colors: [ - UIColor(rgb: 0x54cb68).cgColor, - UIColor(rgb: 0xa0de7e).cgColor - ], - direction: .mirroredDiagonal - ) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Google") - case .fragment: - self.backgroundView.image = generateFilledCircleImage(diameter: size.width, color: UIColor(rgb: 0x1b1f24)) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment") - iconOffset = 2.0 - case .ads: - self.backgroundView.image = generateGradientFilledCircleImage( - diameter: size.width, - colors: [ - UIColor(rgb: 0xffa85c).cgColor, - UIColor(rgb: 0xffcd6a).cgColor - ], - direction: .mirroredDiagonal - ) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white) - case .premiumBot: - iconInset = 7.0 - self.backgroundView.image = generateGradientFilledCircleImage( - diameter: size.width, - colors: [ - UIColor(rgb: 0x6b93ff).cgColor, - UIColor(rgb: 0x6b93ff).cgColor, - UIColor(rgb: 0x8d77ff).cgColor, - UIColor(rgb: 0xb56eec).cgColor, - UIColor(rgb: 0xb56eec).cgColor - ], - direction: .mirroredDiagonal - ) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white) - case .apiLimitExtension: - self.backgroundView.image = generateGradientFilledCircleImage( - diameter: size.width, - colors: [ - UIColor(rgb: 0x32b83b).cgColor, - UIColor(rgb: 0x87d93b).cgColor - ], direction: .vertical ) self.backgroundView.isHidden = false self.iconView.isHidden = false self.avatarNode.isHidden = true - self.iconView.image = UIImage(bundleImageName: "Premium/Stars/PaidBroadcast") - case .unsupported: - iconInset = 7.0 - self.backgroundView.image = generateGradientFilledCircleImage( - diameter: size.width, - colors: [ - UIColor(rgb: 0xb1b1b1).cgColor, - UIColor(rgb: 0xcdcdcd).cgColor - ], - direction: .mirroredDiagonal - ) - self.backgroundView.isHidden = false - self.iconView.isHidden = false - self.avatarNode.isHidden = true - self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white) + self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchInlineButtonIcon"), color: .white) } self.avatarNode.frame = CGRect(origin: .zero, size: size) diff --git a/submodules/TelegramUI/Components/Stars/StarsImageComponent/Sources/StarsImageComponent.swift b/submodules/TelegramUI/Components/Stars/StarsImageComponent/Sources/StarsImageComponent.swift index fe3287bfe5..1aea568c66 100644 --- a/submodules/TelegramUI/Components/Stars/StarsImageComponent/Sources/StarsImageComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsImageComponent/Sources/StarsImageComponent.swift @@ -255,6 +255,7 @@ public final class StarsImageComponent: Component { case transactionPeer(StarsContext.State.Transaction.Peer) case gift(Int32) case color(UIColor) + case search public static func == (lhs: StarsImageComponent.Subject, rhs: StarsImageComponent.Subject) -> Bool { switch lhs { @@ -300,6 +301,12 @@ public final class StarsImageComponent: Component { } else { return false } + case .search: + if case .search = rhs { + return true + } else { + return false + } } } } @@ -880,6 +887,36 @@ public final class StarsImageComponent: Component { let animationFrame = imageFrame.insetBy(dx: -imageFrame.width * 0.19, dy: -imageFrame.height * 0.19).offsetBy(dx: 0.0, dy: -14.0) animationNode.frame = animationFrame animationNode.updateLayout(size: animationFrame.size) + case .search: + let iconBackgroundView: UIImageView + let iconView: UIImageView + if let currentBackground = self.iconBackgroundView, let current = self.iconView { + iconBackgroundView = currentBackground + iconView = current + } else { + iconBackgroundView = UIImageView() + iconView = UIImageView() + + containerNode.view.addSubview(iconBackgroundView) + containerNode.view.addSubview(iconView) + + self.iconBackgroundView = iconBackgroundView + self.iconView = iconView + } + + let iconInset: CGFloat = 11.0 + let iconOffset: CGFloat = 0.0 + iconBackgroundView.image = generateGradientFilledCircleImage( + diameter: imageSize.width, + colors: [ + UIColor(rgb: 0x2a9ef1).cgColor, + UIColor(rgb: 0x72d5fd).cgColor + ], + direction: .vertical + ) + iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchInlineButtonIcon"), color: .white) + iconBackgroundView.frame = imageFrame + iconView.frame = imageFrame.insetBy(dx: iconInset, dy: iconInset).offsetBy(dx: 0.0, dy: iconOffset) } if let icon = component.icon { diff --git a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift index 37da22d283..97e399a610 100644 --- a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift @@ -331,7 +331,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent { continue } - let title = strings.Stars_Purchase_Stars(Int32(product.count)) + let title = strings.Stars_Purchase_Stars(Int32(clamping: product.count)) let price = product.price let titleComponent = AnyComponent(MultilineTextComponent( diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index d72766b9f7..846115bada 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -245,6 +245,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { var giftAvailability: StarGift.Gift.Availability? var isRefProgram = false var isPaidMessage = false + var isPostsSearch = false var premiumGiftMonths: Int32? var delayedCloseOnOpenPeer = true @@ -254,7 +255,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { fatalError() } let boosts = boost.multiplier - titleText = strings.Stars_Transaction_Giveaway_Boost_Stars(Int32(stars)) + titleText = strings.Stars_Transaction_Giveaway_Boost_Stars(Int32(clamping: stars)) descriptionText = "" boostsText = strings.Stars_Transaction_Giveaway_Boost_Boosts(boosts) count = CurrencyAmount(amount: StarsAmount(value: stars, nanos: 0), currency: .stars) @@ -471,6 +472,13 @@ private final class StarsTransactionSheetContent: CombinedComponent { } transactionPeer = transaction.peer isReaction = true + } else if transaction.flags.contains(.isPostsSearch) { + titleText = strings.Stars_Transaction_SearchFee_Title + descriptionText = "" + count = transaction.count + transactionId = transaction.id + date = transaction.date + isPostsSearch = true } else { switch transaction.peer { case let .peer(peer): @@ -497,14 +505,29 @@ private final class StarsTransactionSheetContent: CombinedComponent { case .fragment: if parentPeer.id == component.context.account.peerId { if (transaction.count.amount.value < 0 && !transaction.flags.contains(.isRefund)) || (transaction.count.amount.value > 0 && transaction.flags.contains(.isRefund)) { - titleText = strings.Stars_Transaction_FragmentWithdrawal_Title + switch transaction.count.currency { + case .stars: + titleText = strings.Stars_Transaction_FragmentWithdrawal_Title + case .ton: + titleText = strings.Stars_Transaction_FragmentWithdrawalTon_Title + } via = strings.Stars_Transaction_FragmentWithdrawal_Subtitle } else { - titleText = strings.Stars_Transaction_FragmentTopUp_Title + switch transaction.count.currency { + case .stars: + titleText = strings.Stars_Transaction_FragmentTopUp_Title + case .ton: + titleText = strings.Stars_Transaction_FragmentTopUpTon_Title + } via = strings.Stars_Transaction_FragmentTopUp_Subtitle } } else { - titleText = strings.Stars_Transaction_FragmentWithdrawal_Title + switch transaction.count.currency { + case .stars: + titleText = strings.Stars_Transaction_FragmentWithdrawal_Title + case .ton: + titleText = strings.Stars_Transaction_FragmentWithdrawalTon_Title + } via = strings.Stars_Transaction_FragmentWithdrawal_Subtitle } case .ads: @@ -718,7 +741,9 @@ private final class StarsTransactionSheetContent: CombinedComponent { let imageSubject: StarsImageComponent.Subject var imageIcon: StarsImageComponent.Icon? - if let premiumGiftMonths { + if isPostsSearch { + imageSubject = .search + } else if let premiumGiftMonths { imageSubject = .gift(premiumGiftMonths) } else if isGift { var value: Int32 = 3 @@ -1034,7 +1059,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { id: "prize", title: strings.Stars_Transaction_Giveaway_Prize, component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Stars_Transaction_Giveaway_Stars(Int32(count.amount.value)), font: tableFont, textColor: tableTextColor))) + MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Stars_Transaction_Giveaway_Stars(Int32(clamping: count.amount.value)), font: tableFont, textColor: tableTextColor))) ) )) @@ -2379,7 +2404,7 @@ private final class PeerCellComponent: Component { let avatarNaturalSize = self.avatar.update( transition: .immediate, component: AnyComponent( - StarsAvatarComponent(context: component.context, theme: component.theme, peer: peer, photo: nil, media: [], uniqueGift: nil, backgroundColor: .clear) + StarsAvatarComponent(context: component.context, theme: component.theme, peer: .transactionPeer(peer), photo: nil, media: [], uniqueGift: nil, backgroundColor: .clear) ), environment: {}, containerSize: CGSize(width: 40.0, height: 40.0) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index 911bcfa45f..5156bf757c 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -924,7 +924,7 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { scale: 0.066, colors: [:], title: presentationData.strings.Stars_Intro_PurchasedTitle, - text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string, + text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(clamping: stars))).string, customUndoText: nil, timeout: nil ), diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift index 768dfd30c5..319a099884 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift @@ -299,7 +299,7 @@ final class StarsTransactionsListPanelComponent: Component { var itemTitle: String let itemSubtitle: String? var itemDate: String - var itemPeer = item.peer + var itemPeer: StarsAvatarComponent.Peer = .transactionPeer(item.peer) var itemFile: TelegramMediaFile? var uniqueGift: StarGift.UniqueGift? switch item.peer { @@ -307,6 +307,10 @@ final class StarsTransactionsListPanelComponent: Component { if let months = item.premiumGiftMonths { itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast) itemSubtitle = environment.strings.Stars_Intro_Transaction_TelegramPremium(months) + } else if item.flags.contains(.isPostsSearch) { + itemTitle = environment.strings.Stars_Intro_Transaction_SearchFee + itemSubtitle = "" + itemPeer = .search } else if item.flags.contains(.isPaidMessage) { itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast) itemSubtitle = environment.strings.Stars_Intro_Transaction_PaidMessage(item.paidMessageCount ?? 1) @@ -371,7 +375,7 @@ final class StarsTransactionsListPanelComponent: Component { if item.flags.contains(.isGift) { itemTitle = environment.strings.Stars_Intro_Transaction_Gift_UnknownUser itemSubtitle = environment.strings.Stars_Intro_Transaction_Gift_Title - itemPeer = .fragment + itemPeer = .transactionPeer(.fragment) } else { if (item.count.amount.value < 0 && !item.flags.contains(.isRefund)) || (item.count.amount.value > 0 && item.flags.contains(.isRefund)) { itemTitle = environment.strings.Stars_Intro_Transaction_FragmentWithdrawal_Title diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 7456e6fe8b..0c5af23a24 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -969,7 +969,7 @@ final class StarsTransactionsScreenComponent: Component { theme: environment.theme, title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)), contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0), - leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: .peer(subscription.peer), photo: nil, media: [], uniqueGift: nil, backgroundColor: environment.theme.list.plainBackgroundColor))), false), + leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: .transactionPeer(.peer(subscription.peer)), photo: nil, media: [], uniqueGift: nil, backgroundColor: environment.theme.list.plainBackgroundColor))), false), icon: nil, accessory: .custom(ListActionItemComponent.CustomAccessory(component: labelComponent, insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))), action: { [weak self] _ in @@ -1336,7 +1336,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { scale: 0.066, colors: [:], title: presentationData.strings.Stars_Intro_PurchasedTitle, - text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string, + text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(clamping: stars))).string, customUndoText: nil, timeout: nil ), diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index 0dafcc6410..195dffb752 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -413,9 +413,9 @@ private final class SheetContent: CombinedComponent { let amount = component.invoice.totalAmount let infoText: String if case .starsChatSubscription = context.component.source { - infoText = strings.Stars_Transfer_SubscribeInfo(state.botPeer?.compactDisplayTitle ?? "", strings.Stars_Transfer_Info_Stars(Int32(amount))).string + infoText = strings.Stars_Transfer_SubscribeInfo(state.botPeer?.compactDisplayTitle ?? "", strings.Stars_Transfer_Info_Stars(Int32(clamping: amount))).string } else if let _ = component.invoice.subscriptionPeriod { - infoText = strings.Stars_Transfer_BotSubscribeInfo(component.invoice.title, state.botPeer?.compactDisplayTitle ?? "", strings.Stars_Transfer_BotSubscribeInfo_Stars(Int32(amount))).string + infoText = strings.Stars_Transfer_BotSubscribeInfo(component.invoice.title, state.botPeer?.compactDisplayTitle ?? "", strings.Stars_Transfer_BotSubscribeInfo_Stars(Int32(clamping: amount))).string } else if !component.extendedMedia.isEmpty { var description: String = "" var photoCount: Int32 = 0 @@ -447,26 +447,26 @@ private final class SheetContent: CombinedComponent { infoText = strings.Stars_Transfer_UnlockBotInfo( description, authorPeerName, - strings.Stars_Transfer_Info_Stars(Int32(amount)) + strings.Stars_Transfer_Info_Stars(Int32(clamping: amount)) ).string } else if let botPeerName = state.botPeer?.compactDisplayTitle { infoText = strings.Stars_Transfer_UnlockBotInfo( description, botPeerName, - strings.Stars_Transfer_Info_Stars(Int32(amount)) + strings.Stars_Transfer_Info_Stars(Int32(clamping: amount)) ).string } else { infoText = strings.Stars_Transfer_UnlockInfo( description, state.chatPeer?.compactDisplayTitle ?? "", - strings.Stars_Transfer_Info_Stars(Int32(amount)) + strings.Stars_Transfer_Info_Stars(Int32(clamping: amount)) ).string } } else { infoText = strings.Stars_Transfer_Info( component.invoice.title, state.botPeer?.compactDisplayTitle ?? "", - strings.Stars_Transfer_Info_Stars(Int32(amount)) + strings.Stars_Transfer_Info_Stars(Int32(clamping: amount)) ).string } @@ -611,11 +611,11 @@ private final class SheetContent: CombinedComponent { let text: String if isSubscription { title = presentationData.strings.Stars_Transfer_Subscribe_Successful_Title - text = presentationData.strings.Stars_Transfer_Subscribe_Successful_Text(presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount)), botTitle).string + text = presentationData.strings.Stars_Transfer_Subscribe_Successful_Text(presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(clamping: invoice.totalAmount)), botTitle).string } else if let _ = component.invoice.extendedMedia { - text = presentationData.strings.Stars_Transfer_UnlockedText( presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string + text = presentationData.strings.Stars_Transfer_UnlockedText( presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(clamping: invoice.totalAmount))).string } else { - text = presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string + text = presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(clamping: invoice.totalAmount))).string } if let navigationController = controller?.navigationController { diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 5cf28288db..5bf3c37110 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -1228,9 +1228,9 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer { func presentMinAmountTooltip(_ minAmount: Int64, currency: CurrencyAmount.Currency) { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - var text = presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(minAmount))).string + var text = presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(clamping: minAmount))).string if case .starGiftResell = self.mode { - text = presentationData.strings.Stars_SellGiftMinAmountToast_Text("\(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(minAmount)))").string + text = presentationData.strings.Stars_SellGiftMinAmountToast_Text("\(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(clamping: minAmount)))").string } else if case let .suggestedPost(mode, _, _, _) = self.mode { let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) switch currency { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift index 04d2132065..62620a0dff 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift @@ -111,7 +111,7 @@ extension ChatControllerImpl { } let title = self.presentationData.strings.Chat_PaidMessage_Sent_Title(count) - let text = self.presentationData.strings.Chat_PaidMessage_Sent_Text(self.presentationData.strings.Chat_PaidMessage_Sent_Text_Stars(Int32(amount.value * Int64(count)))).string + let text = self.presentationData.strings.Chat_PaidMessage_Sent_Text(self.presentationData.strings.Chat_PaidMessage_Sent_Text_Stars(Int32(clamping: amount.value * Int64(count)))).string let textItems: [AnimatedTextComponent.Item] = [ AnimatedTextComponent.Item(id: 0, content: .text(text)) ] diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index d7a501a297..0290a0d907 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -756,6 +756,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur var profile: Bool = false var referrer: String? var albumId: Int64? + var collectionId: Int64? if let queryItems = components.queryItems { for queryItem in queryItems { if let value = queryItem.value { @@ -793,6 +794,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur referrer = value } else if queryItem.name == "album" { albumId = Int64(value) + } else if queryItem.name == "collection" { + collectionId = Int64(value) } } else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) { voiceChat = "" @@ -866,6 +869,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur result += "?attach=\(attach)" } else if let albumId { result += "/a/\(albumId)" + } else if let collectionId { + result += "/c/\(collectionId)" } if let startAttach = startAttach { if attach == nil {