From 24e01cc8d47e4027b33023ce9cc5c823aa213e0f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 27 Aug 2025 21:51:58 +0400 Subject: [PATCH] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 21 ++++ .../Sources/GiftValueScreen.swift | 31 +++--- .../Sources/GiftViewScreen.swift | 105 +++++++++++++++--- 3 files changed, 126 insertions(+), 31 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 1f3ab2992f..7d1706c435 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14973,3 +14973,24 @@ Sorry for the inconvenience."; "Ton.WithdrawViaFragment.Info_URL" = "https://telegram.org/tos/bot-developers#6-2-2-tpa-balance"; "MESSAGE_GIFT_THEME" = "%1$@ changed theme to %2$@"; + +"Gift.Upgrade.Skip" = "Skip"; +"Gift.Upgrade.UpgradeNext" = "Upgrade Next Gift"; + +"Gift.Value.DescriptionAveragePrice" = "This is the average sale price of **%@** on Telegram and Fragment over the past month."; +"Gift.Value.DescriptionLastPriceFragment" = "This is the last price at which **%@** was last sold on Fragment."; +"Gift.Value.DescriptionLastPriceTelegram" = "This is the last price at which **%@** was last sold on Telegram."; + +"Gift.Value.LastPriceInfo" = "**%1$@** is the last price for %2$@ gifts listed on Telegram and Fragment."; +"Gift.Value.MinimumPriceInfo" = "**%1$@** is the floor price for %2$@ gifts listed on Telegram and Fragment."; +"Gift.Value.AveragePriceInfo" = "**%1$@** is the average sale price for %2$@ gifts listed on Telegram and Fragment over the past month."; + +"Gift.Value.AveragePrice" = "Last Sale"; +"Gift.Value.InitialSale" = "Initial Sale"; +"Gift.Value.InitialPrice" = "Initial Price"; +"Gift.Value.LastSale" = "Last Sale"; +"Gift.Value.LastPrice" = "Last Price"; +"Gift.Value.MinimumPrice" = "Minimum Price"; +"Gift.Value.AveragePrice" = "Average Price"; +"Gift.Value.ForSaleOnTelegram" = "for sale on Telegram"; +"Gift.Value.ForSaleOnFragment" = "for sale on Fragment"; diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift index f0b757f8ee..cb1b95c110 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift @@ -205,8 +205,6 @@ private final class GiftValueSheetContent: CombinedComponent { let theme = environment.theme let strings = environment.strings let dateTimeFormat = environment.dateTimeFormat - //let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder - //let controller = environment.controller let state = context.state @@ -319,12 +317,12 @@ private final class GiftValueSheetContent: CombinedComponent { var descriptionText: String if component.valueInfo.valueIsAverage { - descriptionText = "This is the average sale price of **\(giftCollectionTitle)** on Telegram and Fragment over the past month." + descriptionText = strings.Gift_Value_DescriptionAveragePrice(giftCollectionTitle).string } else { if component.valueInfo.isLastSaleOnFragment { - descriptionText = "This is the last price at which **\(giftTitle)** was last sold on Fragment." + descriptionText = strings.Gift_Value_DescriptionLastPriceFragment(giftTitle).string } else { - descriptionText = "This is the last price at which **\(giftTitle)** was last sold on Telegram." + descriptionText = strings.Gift_Value_DescriptionLastPriceTelegram(giftTitle).string } } if !descriptionText.isEmpty { @@ -394,7 +392,7 @@ private final class GiftValueSheetContent: CombinedComponent { tableItems.append(.init( id: "initialDate", - title: "Initial Sale", + title: strings.Gift_Value_InitialSale, component: AnyComponent( MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: component.valueInfo.initialSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor))) ) @@ -410,7 +408,7 @@ private final class GiftValueSheetContent: CombinedComponent { tableItems.append(.init( id: "initialPrice", - title: "Initial Price", + title: strings.Gift_Value_InitialPrice, component: AnyComponent(MultilineTextWithEntitiesComponent( context: component.context, animationCache: component.context.animationCache, @@ -425,7 +423,7 @@ private final class GiftValueSheetContent: CombinedComponent { if let lastSaleDate = component.valueInfo.lastSaleDate { tableItems.append(.init( id: "lastDate", - title: "Last Sale", + title: strings.Gift_Value_LastSale, component: AnyComponent( MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: lastSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor))) ) @@ -457,7 +455,7 @@ private final class GiftValueSheetContent: CombinedComponent { color: theme.list.itemAccentColor )), action: { [weak state] in - state?.showAttributeInfo(tag: tag, text: "**\(lastSalePriceString)** is the last price for \(giftCollectionTitle) gifts listed on Telegram and Fragment.") + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_LastPriceInfo(lastSalePriceString, giftCollectionTitle).string) } ).tagged(tag)) @@ -467,7 +465,7 @@ private final class GiftValueSheetContent: CombinedComponent { ) tableItems.append(.init( id: "lastPrice", - title: "Last Price", + title: strings.Gift_Value_LastPrice, hasBackground: false, component: itemComponent )) @@ -494,8 +492,7 @@ private final class GiftValueSheetContent: CombinedComponent { color: theme.list.itemAccentColor )), action: { [weak state] in - state?.showAttributeInfo(tag: tag, text: "**\(floorPriceString)** is the floor price for \(giftCollectionTitle) gifts listed on Telegram and Fragment.") - + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_MinimumPriceInfo(floorPriceString, giftCollectionTitle).string) } ).tagged(tag)) )) @@ -504,7 +501,7 @@ private final class GiftValueSheetContent: CombinedComponent { ) tableItems.append(.init( id: "floorPrice", - title: "Minumum Price", + title: strings.Gift_Value_MinimumPrice, hasBackground: false, component: itemComponent )) @@ -531,7 +528,7 @@ private final class GiftValueSheetContent: CombinedComponent { color: theme.list.itemAccentColor )), action: { [weak state] in - state?.showAttributeInfo(tag: tag, text: "**\(averagePriceString)** is the average sale price of \(giftCollectionTitle) on Telegram and Fragment over the past month.") + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_AveragePriceInfo(averagePriceString, giftCollectionTitle).string) } ).tagged(tag)) )) @@ -540,7 +537,7 @@ private final class GiftValueSheetContent: CombinedComponent { ) tableItems.append(.init( id: "averagePrice", - title: "Average Price", + title: strings.Gift_Value_AveragePrice, hasBackground: false, component: itemComponent )) @@ -587,7 +584,7 @@ private final class GiftValueSheetContent: CombinedComponent { ) )), AnyComponentWithIdentity(id: "label", component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: " for sale on Telegram", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnTelegram)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) )), AnyComponentWithIdentity(id: "arrow", component: AnyComponent( BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor) @@ -639,7 +636,7 @@ private final class GiftValueSheetContent: CombinedComponent { ) )), AnyComponentWithIdentity(id: "label", component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: " for sale on Fragment", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnFragment)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) )), AnyComponentWithIdentity(id: "arrow", component: AnyComponent( BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 422cb574c2..d5a0f3cc06 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -104,6 +104,7 @@ private final class GiftViewSheetContent: CombinedComponent { var cachedHiddenImage: (UIImage, PresentationTheme)? var inProgress = false + var canSkip = false var testUpgradeAnimation = !"".isEmpty @@ -668,14 +669,32 @@ private final class GiftViewSheetContent: CombinedComponent { return } self.isOpeningValue = true + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let _ = (self.context.engine.payments.getUniqueStarGiftValueInfo(slug: uniqueGift.slug) |> deliverOnMainQueue).start(next: { [weak self] valueInfo in - guard let self, let valueInfo else { + guard let self else { return } self.isOpeningValue = false - let valueController = GiftValueScreen(context: self.context, gift: gift, valueInfo: valueInfo) - controller.push(valueController) + if let valueInfo { + let valueController = GiftValueScreen(context: self.context, gift: gift, valueInfo: valueInfo) + controller.push(valueController) + } else { + guard let controller = self.getController() as? GiftViewScreen else { + return + } + let alertController = textAlertController( + context: self.context, + title: nil, + text: presentationData.strings.Login_UnknownError, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ], + parseMarkdown: true + ) + controller.present(alertController, in: .window(.root)) + } }) } @@ -1528,7 +1547,28 @@ private final class GiftViewSheetContent: CombinedComponent { } } + func skipAnimation() { + guard let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift else { + return + } + self.canSkip = false + self.revealedNumberDigits = "\(uniqueGift.number)".count + self.revealedAttributes.insert(.backdrop) + self.revealedAttributes.insert(.pattern) + self.revealedAttributes.insert(.model) + + self.updated(transition: .easeInOut(duration: 0.2)) + } + func commitUpgrade() { + let duration = Double.random(in: 0.85 ..< 2.25) + let firstFraction = Double.random(in: 0.2 ..< 0.4) + let secondFraction = Double.random(in: 0.2 ..< 0.4) + let thirdFraction = 1.0 - firstFraction - secondFraction + let firstDuration = duration * firstFraction + let secondDuration = duration * secondFraction + let thirdDuration = duration * thirdFraction + if self.testUpgradeAnimation, let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift { self.inProgress = true self.updated() @@ -1538,10 +1578,14 @@ private final class GiftViewSheetContent: CombinedComponent { } Queue.mainQueue().after(0.5, { + self.canSkip = true + self.updated(transition: .immediate) + self.inUpgradePreview = false self.inProgress = false self.justUpgraded = true + self.revealedNumberDigits = -1 for i in 0 ..< "\(uniqueGift.number)".count { @@ -1552,17 +1596,22 @@ private final class GiftViewSheetContent: CombinedComponent { } self.updated(transition: .spring(duration: 0.4)) - Queue.mainQueue().after(1.2) { + Queue.mainQueue().after(firstDuration) { self.revealedAttributes.insert(.backdrop) self.updated(transition: .immediate) - Queue.mainQueue().after(0.7) { + Queue.mainQueue().after(secondDuration) { self.revealedAttributes.insert(.pattern) self.updated(transition: .immediate) - Queue.mainQueue().after(0.7) { + Queue.mainQueue().after(thirdDuration) { self.revealedAttributes.insert(.model) self.updated(transition: .immediate) + + Queue.mainQueue().after(0.55) { + self.canSkip = false + self.updated(transition: .easeInOut(duration: 0.2)) + } Queue.mainQueue().after(0.6) { if let controller = self.getController() as? GiftViewScreen { @@ -1619,6 +1668,9 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self, let controller = self.getController() as? GiftViewScreen else { return } + self.canSkip = true + self.updated(transition: .immediate) + self.inProgress = false self.inUpgradePreview = false @@ -1638,18 +1690,23 @@ private final class GiftViewSheetContent: CombinedComponent { } } } - - Queue.mainQueue().after(1.2) { + + Queue.mainQueue().after(firstDuration) { self.revealedAttributes.insert(.backdrop) self.updated(transition: .immediate) - Queue.mainQueue().after(0.7) { + Queue.mainQueue().after(secondDuration) { self.revealedAttributes.insert(.pattern) self.updated(transition: .immediate) - Queue.mainQueue().after(0.7) { + Queue.mainQueue().after(thirdDuration) { self.revealedAttributes.insert(.model) self.updated(transition: .immediate) + + Queue.mainQueue().after(0.55) { + self.canSkip = false + self.updated(transition: .easeInOut(duration: 0.2)) + } Queue.mainQueue().after(0.6) { if let controller = self.getController() as? GiftViewScreen { @@ -3771,7 +3828,25 @@ private final class GiftViewSheetContent: CombinedComponent { pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) ) let buttonChild: _UpdatedChildComponent - if showWearPreview, let uniqueGift { + if state.canSkip { + buttonChild = button.update( + component: ButtonComponent( + background: buttonBackground, + content: AnyComponentWithIdentity( + id: AnyHashable("skip"), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_Skip, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + isEnabled: true, + displaysProgress: state.inProgress, + action: { [weak state] in + if let state { + state.skipAnimation() + } + }), + availableSize: buttonSize, + transition: context.transition + ) + } else if showWearPreview, let uniqueGift { let buttonContent: AnyComponentWithIdentity let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) @@ -4091,7 +4166,9 @@ private final class GiftViewSheetContent: CombinedComponent { isEnabled: true, displaysProgress: state.inProgress, action: { [weak state] in - state?.dismiss(animated: true) + if let state { + state.dismiss(animated: true) + } }), availableSize: buttonSize, transition: context.transition @@ -4100,7 +4177,7 @@ private final class GiftViewSheetContent: CombinedComponent { let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: buttonChild.size) var buttonAlpha: CGFloat = 1.0 - if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift { + if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift, !state.canSkip { buttonAlpha = 0.0 let upgradeNextButton = upgradeNextButton.update( @@ -4108,7 +4185,7 @@ private final class GiftViewSheetContent: CombinedComponent { content: AnyComponent( HStack([ AnyComponentWithIdentity(id: "label", component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: "Upgrade Next Gift", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_UpgradeNext, font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) )), AnyComponentWithIdentity(id: "icon", component: AnyComponent( GiftItemComponent(