mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Stars slider improvements
This commit is contained in:
parent
4ea0a99778
commit
524ce21f5e
@ -227,6 +227,14 @@ private final class BadgeComponent: Component {
|
|||||||
required init(coder: NSCoder) {
|
required init(coder: NSCoder) {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
if self.badgeView.frame.contains(point) {
|
||||||
|
return self
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: BadgeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
func update(component: BadgeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
@ -806,7 +814,7 @@ private final class SliderBackgroundComponent: Component {
|
|||||||
topBackgroundTextView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: animateTopTextAdditionalX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.3, damping: 100.0, additive: true)
|
topBackgroundTextView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: animateTopTextAdditionalX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.3, damping: 100.0, additive: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
topForegroundTextView.isHidden = component.topCutoff == nil
|
topForegroundTextView.isHidden = component.topCutoff == nil || topLineFrame.maxX + topTextSize.width + 20.0 > availableSize.width
|
||||||
topBackgroundTextView.isHidden = topForegroundTextView.isHidden
|
topBackgroundTextView.isHidden = topForegroundTextView.isHidden
|
||||||
self.topBackgroundLine.isHidden = topX < 10.0
|
self.topBackgroundLine.isHidden = topX < 10.0
|
||||||
self.topForegroundLine.isHidden = self.topBackgroundLine.isHidden
|
self.topForegroundLine.isHidden = self.topBackgroundLine.isHidden
|
||||||
@ -915,6 +923,83 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct Amount: Equatable {
|
||||||
|
private let sliderSteps: [Int]
|
||||||
|
private let maxRealValue: Int
|
||||||
|
let maxSliderValue: Int
|
||||||
|
private let isLogarithmic: Bool
|
||||||
|
|
||||||
|
private(set) var realValue: Int
|
||||||
|
private(set) var sliderValue: Int
|
||||||
|
|
||||||
|
private static func makeSliderSteps(maxRealValue: Int, isLogarithmic: Bool) -> [Int] {
|
||||||
|
if isLogarithmic {
|
||||||
|
var sliderSteps: [Int] = [ 1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ]
|
||||||
|
sliderSteps.removeAll(where: { $0 >= maxRealValue })
|
||||||
|
sliderSteps.append(maxRealValue)
|
||||||
|
return sliderSteps
|
||||||
|
} else {
|
||||||
|
return [1, maxRealValue]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func remapValueToSlider(realValue: Int, maxSliderValue: Int, steps: [Int]) -> Int {
|
||||||
|
guard realValue >= steps.first!, realValue <= steps.last! else { return 0 }
|
||||||
|
|
||||||
|
for i in 0 ..< steps.count - 1 {
|
||||||
|
if realValue >= steps[i] && realValue <= steps[i + 1] {
|
||||||
|
let range = steps[i + 1] - steps[i]
|
||||||
|
let relativeValue = realValue - steps[i]
|
||||||
|
let stepFraction = Float(relativeValue) / Float(range)
|
||||||
|
return Int(Float(i) * Float(maxSliderValue) / Float(steps.count - 1)) + Int(stepFraction * Float(maxSliderValue) / Float(steps.count - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxSliderValue // Return max slider position if value equals the last step
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func remapSliderToValue(sliderValue: Int, maxSliderValue: Int, steps: [Int]) -> Int {
|
||||||
|
guard sliderValue >= 0, sliderValue <= maxSliderValue else { return steps.first! }
|
||||||
|
|
||||||
|
let stepIndex = Int(Float(sliderValue) / Float(maxSliderValue) * Float(steps.count - 1))
|
||||||
|
let fraction = Float(sliderValue) / Float(maxSliderValue) * Float(steps.count - 1) - Float(stepIndex)
|
||||||
|
|
||||||
|
if stepIndex >= steps.count - 1 {
|
||||||
|
return steps.last!
|
||||||
|
} else {
|
||||||
|
let range = steps[stepIndex + 1] - steps[stepIndex]
|
||||||
|
return steps[stepIndex] + Int(fraction * Float(range))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(realValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) {
|
||||||
|
self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic)
|
||||||
|
self.maxRealValue = maxRealValue
|
||||||
|
self.maxSliderValue = maxSliderValue
|
||||||
|
self.isLogarithmic = isLogarithmic
|
||||||
|
|
||||||
|
self.realValue = realValue
|
||||||
|
self.sliderValue = Amount.remapValueToSlider(realValue: self.realValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(sliderValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) {
|
||||||
|
self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic)
|
||||||
|
self.maxRealValue = maxRealValue
|
||||||
|
self.maxSliderValue = maxSliderValue
|
||||||
|
self.isLogarithmic = isLogarithmic
|
||||||
|
|
||||||
|
self.sliderValue = sliderValue
|
||||||
|
self.realValue = Amount.remapSliderToValue(sliderValue: self.sliderValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withRealValue(_ realValue: Int) -> Amount {
|
||||||
|
return Amount(realValue: realValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withSliderValue(_ sliderValue: Int) -> Amount {
|
||||||
|
return Amount(sliderValue: sliderValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class View: UIView, UIScrollViewDelegate {
|
final class View: UIView, UIScrollViewDelegate {
|
||||||
private let dimView: UIView
|
private let dimView: UIView
|
||||||
private let backgroundLayer: SimpleLayer
|
private let backgroundLayer: SimpleLayer
|
||||||
@ -959,7 +1044,9 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
private var topOffsetDistance: CGFloat?
|
private var topOffsetDistance: CGFloat?
|
||||||
|
|
||||||
private var balance: Int64?
|
private var balance: Int64?
|
||||||
private var amount: Int64 = 1
|
|
||||||
|
private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true)
|
||||||
|
|
||||||
private var isAnonymous: Bool = false
|
private var isAnonymous: Bool = false
|
||||||
private var cachedStarImage: (UIImage, PresentationTheme)?
|
private var cachedStarImage: (UIImage, PresentationTheme)?
|
||||||
private var cachedCloseImage: UIImage?
|
private var cachedCloseImage: UIImage?
|
||||||
@ -1058,6 +1145,12 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let badgeView = self.badge.view, badgeView.hitTest(self.convert(point, to: badgeView), with: event) != nil {
|
||||||
|
if let sliderView = self.slider.view as? SliderComponent.View, let hitTestTarget = sliderView.hitTestTarget {
|
||||||
|
return hitTestTarget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let result = super.hitTest(point, with: event)
|
let result = super.hitTest(point, with: event)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -1135,7 +1228,11 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
self.balance = component.balance
|
self.balance = component.balance
|
||||||
self.amount = 50
|
var isLogarithmic = true
|
||||||
|
if let data = component.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_stars_reaction_logarithmic_scale"] as? Double {
|
||||||
|
isLogarithmic = Int(value) != 0
|
||||||
|
}
|
||||||
|
self.amount = Amount(realValue: 50, maxRealValue: component.maxAmount, maxSliderValue: 999, isLogarithmic: isLogarithmic)
|
||||||
if let myTopPeer = component.myTopPeer {
|
if let myTopPeer = component.myTopPeer {
|
||||||
self.isAnonymous = myTopPeer.isAnonymous
|
self.isAnonymous = myTopPeer.isAnonymous
|
||||||
}
|
}
|
||||||
@ -1182,8 +1279,8 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
let sliderSize = self.slider.update(
|
let sliderSize = self.slider.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(SliderComponent(
|
component: AnyComponent(SliderComponent(
|
||||||
valueCount: component.maxAmount,
|
valueCount: self.amount.maxSliderValue + 1,
|
||||||
value: Int(self.amount),
|
value: self.amount.sliderValue,
|
||||||
markPositions: false,
|
markPositions: false,
|
||||||
trackBackgroundColor: .clear,
|
trackBackgroundColor: .clear,
|
||||||
trackForegroundColor: .clear,
|
trackForegroundColor: .clear,
|
||||||
@ -1193,7 +1290,8 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.amount = 1 + Int64(value)
|
self.amount = self.amount.withSliderValue(value)
|
||||||
|
|
||||||
self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint()))
|
self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint()))
|
||||||
|
|
||||||
let sliderValue = Float(value) / Float(component.maxAmount)
|
let sliderValue = Float(value) / Float(component.maxAmount)
|
||||||
@ -1250,7 +1348,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
let sliderFrame = CGRect(origin: CGPoint(x: sliderInset, y: contentHeight + 127.0), size: sliderSize)
|
let sliderFrame = CGRect(origin: CGPoint(x: sliderInset, y: contentHeight + 127.0), size: sliderSize)
|
||||||
let sliderBackgroundFrame = CGRect(origin: CGPoint(x: sliderFrame.minX - 8.0, y: sliderFrame.minY + 7.0), size: CGSize(width: sliderFrame.width + 16.0, height: sliderFrame.height - 14.0))
|
let sliderBackgroundFrame = CGRect(origin: CGPoint(x: sliderFrame.minX - 8.0, y: sliderFrame.minY + 7.0), size: CGSize(width: sliderFrame.width + 16.0, height: sliderFrame.height - 14.0))
|
||||||
|
|
||||||
let progressFraction: CGFloat = CGFloat(self.amount) / CGFloat(component.maxAmount - 1)
|
let progressFraction: CGFloat = CGFloat(self.amount.sliderValue) / CGFloat(self.amount.maxSliderValue)
|
||||||
|
|
||||||
let topOthersCount: Int? = component.topPeers.filter({ !$0.isMy }).max(by: { $0.count < $1.count })?.count
|
let topOthersCount: Int? = component.topPeers.filter({ !$0.isMy }).max(by: { $0.count < $1.count })?.count
|
||||||
var topCount: Int?
|
var topCount: Int?
|
||||||
@ -1310,7 +1408,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(BadgeComponent(
|
component: AnyComponent(BadgeComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
title: "\(self.amount)",
|
title: "\(self.amount.realValue)",
|
||||||
inertiaDirection: effectiveInertiaDirection
|
inertiaDirection: effectiveInertiaDirection
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
@ -1542,7 +1640,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
if let index = mappedTopPeers.firstIndex(where: { $0.isMy }) {
|
if let index = mappedTopPeers.firstIndex(where: { $0.isMy }) {
|
||||||
mappedTopPeers.remove(at: index)
|
mappedTopPeers.remove(at: index)
|
||||||
}
|
}
|
||||||
var myCount = Int(self.amount)
|
var myCount = Int(self.amount.realValue)
|
||||||
if let myTopPeer = component.myTopPeer {
|
if let myTopPeer = component.myTopPeer {
|
||||||
myCount += myTopPeer.count
|
myCount += myTopPeer.count
|
||||||
}
|
}
|
||||||
@ -1747,7 +1845,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
|
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonString = environment.strings.SendStarReactions_SendButtonTitle("\(self.amount)").string
|
let buttonString = environment.strings.SendStarReactions_SendButtonTitle("\(self.amount.realValue)").string
|
||||||
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center)
|
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center)
|
||||||
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
|
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
|
||||||
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
|
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
|
||||||
@ -1778,7 +1876,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if balance < self.amount {
|
if balance < self.amount.realValue {
|
||||||
let _ = (component.context.engine.payments.starsTopUpOptions()
|
let _ = (component.context.engine.payments.starsTopUpOptions()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] options in
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] options in
|
||||||
@ -1789,7 +1887,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let purchaseScreen = component.context.sharedContext.makeStarsPurchaseScreen(context: component.context, starsContext: starsContext, options: options, purpose: .transfer(peerId: component.peer.id, requiredStars: self.amount), completion: { result in
|
let purchaseScreen = component.context.sharedContext.makeStarsPurchaseScreen(context: component.context, starsContext: starsContext, options: options, purpose: .transfer(peerId: component.peer.id, requiredStars: Int64(self.amount.realValue)), completion: { result in
|
||||||
let _ = result
|
let _ = result
|
||||||
//TODO:release
|
//TODO:release
|
||||||
})
|
})
|
||||||
@ -1805,13 +1903,13 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
let isBecomingTop: Bool
|
let isBecomingTop: Bool
|
||||||
if let topCount {
|
if let topCount {
|
||||||
isBecomingTop = self.amount > topCount
|
isBecomingTop = self.amount.realValue > topCount
|
||||||
} else {
|
} else {
|
||||||
isBecomingTop = true
|
isBecomingTop = true
|
||||||
}
|
}
|
||||||
|
|
||||||
component.completion(
|
component.completion(
|
||||||
self.amount,
|
Int64(self.amount.realValue),
|
||||||
self.isAnonymous,
|
self.isAnonymous,
|
||||||
isBecomingTop,
|
isBecomingTop,
|
||||||
ChatSendStarsScreen.TransitionOut(
|
ChatSendStarsScreen.TransitionOut(
|
||||||
|
@ -70,6 +70,10 @@ public final class SliderComponent: Component {
|
|||||||
private var component: SliderComponent?
|
private var component: SliderComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
|
public var hitTestTarget: UIView? {
|
||||||
|
return self.sliderView
|
||||||
|
}
|
||||||
|
|
||||||
override public init(frame: CGRect) {
|
override public init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user