mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Various improvements
This commit is contained in:
@@ -1410,6 +1410,16 @@ public struct StarGiftUpgradePreview: Equatable {
|
|||||||
public let attributes: [StarGift.UniqueGift.Attribute]
|
public let attributes: [StarGift.UniqueGift.Attribute]
|
||||||
public let prices: [Price]
|
public let prices: [Price]
|
||||||
public let nextPrices: [Price]
|
public let nextPrices: [Price]
|
||||||
|
|
||||||
|
public init(attributes: [StarGift.UniqueGift.Attribute], prices: [Price], nextPrices: [Price]) {
|
||||||
|
self.attributes = attributes
|
||||||
|
self.prices = prices
|
||||||
|
self.nextPrices = nextPrices
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withAttributes(_ attributes: [StarGift.UniqueGift.Attribute]) -> StarGiftUpgradePreview {
|
||||||
|
return StarGiftUpgradePreview(attributes: attributes, prices: self.prices, nextPrices: self.nextPrices)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_starGiftUpgradePreview(account: Account, giftId: Int64) -> Signal<StarGiftUpgradePreview?, NoError> {
|
func _internal_starGiftUpgradePreview(account: Account, giftId: Int64) -> Signal<StarGiftUpgradePreview?, NoError> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ swift_library(
|
|||||||
"//submodules/Display",
|
"//submodules/Display",
|
||||||
"//submodules/ComponentFlow",
|
"//submodules/ComponentFlow",
|
||||||
"//submodules/TelegramPresentationData",
|
"//submodules/TelegramPresentationData",
|
||||||
|
"//submodules/Components/BundleIconComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import UIKit
|
|||||||
import Display
|
import Display
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import BundleIconComponent
|
||||||
|
|
||||||
extension ComponentTransition {
|
extension ComponentTransition {
|
||||||
func animateBlur(layer: CALayer, from: CGFloat, to: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
func animateBlur(layer: CALayer, from: CGFloat, to: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||||
@@ -26,6 +27,7 @@ public final class AnimatedTextComponent: Component {
|
|||||||
public enum Content: Equatable {
|
public enum Content: Equatable {
|
||||||
case text(String)
|
case text(String)
|
||||||
case number(Int, minDigits: Int)
|
case number(Int, minDigits: Int)
|
||||||
|
case icon(String, offset: CGPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var id: AnyHashable
|
public var id: AnyHashable
|
||||||
@@ -147,6 +149,9 @@ public final class AnimatedTextComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
itemText = valueText.map(String.init)
|
itemText = valueText.map(String.init)
|
||||||
}
|
}
|
||||||
|
case let .icon(iconName, _):
|
||||||
|
let characterKey = CharacterKey(itemId: item.id, index: 0, value: iconName)
|
||||||
|
validKeys.append(characterKey)
|
||||||
}
|
}
|
||||||
var index = 0
|
var index = 0
|
||||||
for character in itemText {
|
for character in itemText {
|
||||||
@@ -181,13 +186,24 @@ public final class AnimatedTextComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for item in component.items {
|
for item in component.items {
|
||||||
var itemText: [String] = []
|
enum AnimatedTextCharacter {
|
||||||
|
case text(String)
|
||||||
|
case icon(String, CGPoint)
|
||||||
|
|
||||||
|
var value: String {
|
||||||
|
switch self {
|
||||||
|
case let .text(value), let .icon(value, _):
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var itemText: [AnimatedTextCharacter] = []
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .text(text):
|
case let .text(text):
|
||||||
if item.isUnbreakable {
|
if item.isUnbreakable {
|
||||||
itemText = [text]
|
itemText = [.text(text)]
|
||||||
} else {
|
} else {
|
||||||
itemText = text.map(String.init)
|
itemText = text.map { .text(String($0)) }
|
||||||
}
|
}
|
||||||
case let .number(value, minDigits):
|
case let .number(value, minDigits):
|
||||||
var valueText: String = "\(value)"
|
var valueText: String = "\(value)"
|
||||||
@@ -196,14 +212,16 @@ public final class AnimatedTextComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item.isUnbreakable {
|
if item.isUnbreakable {
|
||||||
itemText = [valueText]
|
itemText = [.text(valueText)]
|
||||||
} else {
|
} else {
|
||||||
itemText = valueText.map(String.init)
|
itemText = valueText.map { .text(String($0)) }
|
||||||
}
|
}
|
||||||
|
case let .icon(iconName, offset):
|
||||||
|
itemText = [.icon(iconName, offset)]
|
||||||
}
|
}
|
||||||
var index = 0
|
var index = 0
|
||||||
for character in itemText {
|
for character in itemText {
|
||||||
let characterKey = CharacterKey(itemId: item.id, index: index, value: character)
|
let characterKey = CharacterKey(itemId: item.id, index: index, value: character.value)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
var characterTransition = transition
|
var characterTransition = transition
|
||||||
@@ -216,17 +234,30 @@ public final class AnimatedTextComponent: Component {
|
|||||||
self.characters[characterKey] = characterView
|
self.characters[characterKey] = characterView
|
||||||
}
|
}
|
||||||
|
|
||||||
let characterSize = characterView.update(
|
let characterComponent: AnyComponent<Empty>
|
||||||
transition: characterTransition,
|
var characterOffset: CGPoint = .zero
|
||||||
component: AnyComponent(Text(
|
switch character {
|
||||||
text: String(character),
|
case let .text(text):
|
||||||
|
characterComponent = AnyComponent(Text(
|
||||||
|
text: String(text),
|
||||||
font: component.font,
|
font: component.font,
|
||||||
color: component.color
|
color: component.color
|
||||||
)),
|
))
|
||||||
|
case let .icon(iconName, offset):
|
||||||
|
characterComponent = AnyComponent(BundleIconComponent(
|
||||||
|
name: iconName,
|
||||||
|
tintColor: component.color
|
||||||
|
))
|
||||||
|
characterOffset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
let characterSize = characterView.update(
|
||||||
|
transition: characterTransition,
|
||||||
|
component: characterComponent,
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width, height: 100.0)
|
containerSize: CGSize(width: availableSize.width, height: 100.0)
|
||||||
)
|
)
|
||||||
let characterFrame = CGRect(origin: CGPoint(x: size.width, y: 0.0), size: characterSize)
|
let characterFrame = CGRect(origin: CGPoint(x: size.width + characterOffset.x, y: characterOffset.y), size: characterSize)
|
||||||
if let characterComponentView = characterView.view {
|
if let characterComponentView = characterView.view {
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
if characterComponentView.superview == nil {
|
if characterComponentView.superview == nil {
|
||||||
@@ -358,6 +389,8 @@ public extension AnimatedTextComponent {
|
|||||||
isUnbreakable = true
|
isUnbreakable = true
|
||||||
case .number:
|
case .number:
|
||||||
isUnbreakable = false
|
isUnbreakable = false
|
||||||
|
case .icon:
|
||||||
|
isUnbreakable = true
|
||||||
}
|
}
|
||||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable("\(id)_item_\(range.index)"), isUnbreakable: isUnbreakable, content: value))
|
textItems.append(AnimatedTextComponent.Item(id: AnyHashable("\(id)_item_\(range.index)"), isUnbreakable: isUnbreakable, content: value))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1549,9 +1549,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
private(set) var nextUpgradePrice: StarGiftUpgradePreview.Price?
|
private(set) var nextUpgradePrice: StarGiftUpgradePreview.Price?
|
||||||
|
|
||||||
func upgradePreviewTimerTick() {
|
func upgradePreviewTimerTick() {
|
||||||
guard let upgradePreview = self.upgradePreview else {
|
guard let upgradePreview = self.upgradePreview, let gift = self.subject.arguments?.gift, case let .generic(gift) = gift else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let context = self.context
|
||||||
|
var transition: ComponentTransition = .immediate
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
if let currentPrice = self.effectiveUpgradePrice {
|
if let currentPrice = self.effectiveUpgradePrice {
|
||||||
if let price = upgradePreview.nextPrices.reversed().first(where: { currentTime >= $0.date }) {
|
if let price = upgradePreview.nextPrices.reversed().first(where: { currentTime >= $0.date }) {
|
||||||
@@ -1559,7 +1561,20 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
self.effectiveUpgradePrice = price
|
self.effectiveUpgradePrice = price
|
||||||
if let nextPrice = upgradePreview.nextPrices.first(where: { $0.stars < price.stars }) {
|
if let nextPrice = upgradePreview.nextPrices.first(where: { $0.stars < price.stars }) {
|
||||||
self.nextUpgradePrice = nextPrice
|
self.nextUpgradePrice = nextPrice
|
||||||
|
} else {
|
||||||
|
transition = .spring(duration: 0.4)
|
||||||
|
self.nextUpgradePrice = nil
|
||||||
}
|
}
|
||||||
|
if upgradePreview.nextPrices[upgradePreview.nextPrices.count - 2] == price {
|
||||||
|
self.upgradePreviewDisposable.add((context.engine.payments.starGiftUpgradePreview(giftId: gift.id)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] nextUpgradePreview in
|
||||||
|
guard let self, let nextUpgradePreview else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.upgradePreview = nextUpgradePreview.withAttributes(upgradePreview.attributes)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
self.fetchUpgradeForm()
|
self.fetchUpgradeForm()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1573,7 +1588,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updated()
|
self.updated(transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestUpgradePreview() {
|
func requestUpgradePreview() {
|
||||||
@@ -4483,7 +4498,15 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
|
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
|
||||||
var upgradeString = strings.Gift_Upgrade_Upgrade
|
var upgradeString = strings.Gift_Upgrade_Upgrade
|
||||||
if !incoming {
|
if !incoming {
|
||||||
if let gift = state.starGiftsMap[giftId], let upgradeStars = gift.upgradeStars {
|
let upgradeStars: Int64?
|
||||||
|
if let stars = state.effectiveUpgradePrice?.stars {
|
||||||
|
upgradeStars = stars
|
||||||
|
} else if let gift = state.starGiftsMap[giftId], let stars = gift.upgradeStars {
|
||||||
|
upgradeStars = stars
|
||||||
|
} else {
|
||||||
|
upgradeStars = nil
|
||||||
|
}
|
||||||
|
if let upgradeStars {
|
||||||
let priceString = presentationStringsFormattedNumber(Int32(clamping: upgradeStars), environment.dateTimeFormat.groupingSeparator)
|
let priceString = presentationStringsFormattedNumber(Int32(clamping: upgradeStars), environment.dateTimeFormat.groupingSeparator)
|
||||||
upgradeString = strings.Gift_Upgrade_GiftUpgrade(" # \(priceString)").string
|
upgradeString = strings.Gift_Upgrade_GiftUpgrade(" # \(priceString)").string
|
||||||
}
|
}
|
||||||
@@ -4507,7 +4530,75 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
let upgradeTimeout = nextUpgradePrice.date - currentTime
|
let upgradeTimeout = nextUpgradePrice.date - currentTime
|
||||||
|
|
||||||
buttonTitleItems.append(AnyComponentWithIdentity(id: "static_label", component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))))
|
if let hashIndex = buttonTitle.firstIndex(of: "#") {
|
||||||
|
var buttonAnimatedTitleItems: [AnimatedTextComponent.Item] = []
|
||||||
|
|
||||||
|
var prefix = String(buttonTitle[..<hashIndex])
|
||||||
|
if !prefix.isEmpty {
|
||||||
|
prefix.removeLast()
|
||||||
|
buttonAnimatedTitleItems.append(
|
||||||
|
AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(buttonAnimatedTitleItems.count),
|
||||||
|
content: .text(prefix)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonAnimatedTitleItems.append(
|
||||||
|
AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(buttonAnimatedTitleItems.count),
|
||||||
|
content: .icon("Item List/PremiumIcon", offset: CGPoint(x: 1.0, y: 2.0 + UIScreenPixel))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let suffixStart = buttonTitle.index(after: hashIndex)
|
||||||
|
let suffix = buttonTitle[suffixStart...]
|
||||||
|
|
||||||
|
var i = suffix.startIndex
|
||||||
|
while i < suffix.endIndex {
|
||||||
|
if suffix[i].isNumber {
|
||||||
|
var j = i
|
||||||
|
while j < suffix.endIndex, suffix[j].isNumber {
|
||||||
|
j = suffix.index(after: j)
|
||||||
|
}
|
||||||
|
if let value = Int(suffix[i..<j]) {
|
||||||
|
buttonAnimatedTitleItems.append(
|
||||||
|
AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(buttonAnimatedTitleItems.count),
|
||||||
|
content: .number(value, minDigits: 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
i = j
|
||||||
|
} else {
|
||||||
|
var j = i
|
||||||
|
while j < suffix.endIndex, !suffix[j].isNumber {
|
||||||
|
j = suffix.index(after: j)
|
||||||
|
}
|
||||||
|
let textRun = String(suffix[i..<j])
|
||||||
|
if !textRun.isEmpty {
|
||||||
|
buttonAnimatedTitleItems.append(
|
||||||
|
AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(buttonAnimatedTitleItems.count),
|
||||||
|
content: .text(textRun)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonTitleItems.append(AnyComponentWithIdentity(id: "animated_label", component: AnyComponent(AnimatedTextComponent(
|
||||||
|
font: Font.with(size: 17.0, weight: .semibold, traits: .monospacedNumbers),
|
||||||
|
color: theme.list.itemCheckColors.foregroundColor,
|
||||||
|
items: buttonAnimatedTitleItems,
|
||||||
|
noDelay: true,
|
||||||
|
blur: true
|
||||||
|
))))
|
||||||
|
} else {
|
||||||
|
buttonTitleItems.append(AnyComponentWithIdentity(id: "static_label", component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let minutes = Int(upgradeTimeout / 60)
|
let minutes = Int(upgradeTimeout / 60)
|
||||||
let seconds = Int(upgradeTimeout % 60)
|
let seconds = Int(upgradeTimeout % 60)
|
||||||
|
|||||||
Reference in New Issue
Block a user