Various improvements

This commit is contained in:
Ilya Laktyushin 2023-02-06 18:28:18 +04:00
parent a5bd955643
commit 78b02192cf
7 changed files with 180 additions and 63 deletions

View File

@ -8891,3 +8891,5 @@ Sorry for the inconvenience.";
"Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear."; "Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear.";
"Login.CodeSentCallText" = "Calling **%@** to dictate the code."; "Login.CodeSentCallText" = "Calling **%@** to dictate the code.";
"Premium.Purchase.OnlyOneSubscriptionAllowed" = "You have already purchased Telegram Premium for another account. You can only have one Telegram Premium subscription on one Apple ID.";

View File

@ -308,11 +308,17 @@ public final class InAppPurchaseManager: NSObject {
return signal return signal
} }
public func getValidTransactionIds() -> [String] { public struct ReceiptPurchase: Equatable {
public let productId: String
public let transactionId: String
public let expirationDate: Date
}
public func getReceiptPurchases() -> [ReceiptPurchase] {
guard let data = getReceiptData(), let receipt = parseReceipt(data) else { guard let data = getReceiptData(), let receipt = parseReceipt(data) else {
return [] return []
} }
return receipt.purchases.map { $0.transactionId } return receipt.purchases.map { ReceiptPurchase(productId: $0.productId, transactionId: $0.transactionId, expirationDate: $0.expirationDate) }
} }
} }
@ -359,7 +365,6 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
switch transaction.transactionState { switch transaction.transactionState {
case .purchased: case .purchased:
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
transactionState = .purchased(transactionId: transaction.transactionIdentifier) transactionState = .purchased(transactionId: transaction.transactionIdentifier)
transactionsToAssign.append(transaction) transactionsToAssign.append(transaction)
case .restored: case .restored:

View File

@ -7,6 +7,7 @@ private struct Asn1Tag {
static let sequence: Int32 = 0x10 static let sequence: Int32 = 0x10
static let set: Int32 = 0x11 static let set: Int32 = 0x11
static let utf8String: Int32 = 0x0c static let utf8String: Int32 = 0x0c
static let date: Int32 = 0x16
} }
private struct Asn1Entry { private struct Asn1Entry {
@ -124,10 +125,12 @@ struct Receipt {
fileprivate struct Tag { fileprivate struct Tag {
static let productIdentifier: Int32 = 1702 static let productIdentifier: Int32 = 1702
static let transactionIdentifier: Int32 = 1703 static let transactionIdentifier: Int32 = 1703
static let expirationDate: Int32 = 1708
} }
let productId: String let productId: String
let transactionId: String let transactionId: String
let expirationDate: Date
} }
let purchases: [Purchase] let purchases: [Purchase]
@ -192,6 +195,27 @@ func parseReceipt(_ data: Data) -> Receipt? {
return Receipt(purchases: purchases) return Receipt(purchases: purchases)
} }
private func parseRfc3339Date(_ str: String) -> Date? {
let posixLocale = Locale(identifier: "en_US_POSIX")
let formatter1 = DateFormatter()
formatter1.locale = posixLocale
formatter1.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssX5"
formatter1.timeZone = TimeZone(secondsFromGMT: 0)
let result = formatter1.date(from: str)
if result != nil {
return result
}
let formatter2 = DateFormatter()
formatter2.locale = posixLocale
formatter2.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSX5"
formatter2.timeZone = TimeZone(secondsFromGMT: 0)
return formatter2.date(from: str)
}
private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? { private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? {
let root = parse(data) let root = parse(data)
guard root.tag == Asn1Tag.set else { guard root.tag == Asn1Tag.set else {
@ -200,6 +224,7 @@ private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? {
var productId: String? var productId: String?
var transactionId: String? var transactionId: String?
var expirationDate: Date?
let receiptAttributes = parseSequence(root.data) let receiptAttributes = parseSequence(root.data)
for attribute in receiptAttributes { for attribute in receiptAttributes {
@ -219,12 +244,16 @@ private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? {
let valEntry = parse(value) let valEntry = parse(value)
guard valEntry.tag == Asn1Tag.utf8String else { return nil } guard valEntry.tag == Asn1Tag.utf8String else { return nil }
transactionId = String(bytes: valEntry.data, encoding: .utf8) transactionId = String(bytes: valEntry.data, encoding: .utf8)
case Receipt.Purchase.Tag.expirationDate:
let valEntry = parse(value)
guard valEntry.tag == Asn1Tag.date else { return nil }
expirationDate = parseRfc3339Date(String(bytes: valEntry.data, encoding: .utf8) ?? "")
default: default:
break break
} }
} }
guard let productId, let transactionId else { guard let productId, let transactionId, let expirationDate else {
return nil return nil
} }
return Receipt.Purchase(productId: productId, transactionId: transactionId) return Receipt.Purchase(productId: productId, transactionId: transactionId, expirationDate: expirationDate)
} }

View File

@ -1196,14 +1196,28 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
let otherPeerName: String? let otherPeerName: String?
let products: [PremiumProduct]? let products: [PremiumProduct]?
let selectedProductId: String? let selectedProductId: String?
let validTransactionIds: [String] let validPurchases: [InAppPurchaseManager.ReceiptPurchase]
let promoConfiguration: PremiumPromoConfiguration? let promoConfiguration: PremiumPromoConfiguration?
let present: (ViewController) -> Void let present: (ViewController) -> Void
let selectProduct: (String) -> Void let selectProduct: (String) -> Void
let buy: () -> Void let buy: () -> Void
let updateIsFocused: (Bool) -> Void let updateIsFocused: (Bool) -> Void
init(context: AccountContext, source: PremiumSource, isPremium: Bool?, justBought: Bool, otherPeerName: String?, products: [PremiumProduct]?, selectedProductId: String?, validTransactionIds: [String], promoConfiguration: PremiumPromoConfiguration?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void, buy: @escaping () -> Void, updateIsFocused: @escaping (Bool) -> Void) { init(
context: AccountContext,
source: PremiumSource,
isPremium: Bool?,
justBought: Bool,
otherPeerName: String?,
products: [PremiumProduct]?,
selectedProductId: String?,
validPurchases: [InAppPurchaseManager.ReceiptPurchase],
promoConfiguration: PremiumPromoConfiguration?,
present: @escaping (ViewController) -> Void,
selectProduct: @escaping (String) -> Void,
buy: @escaping () -> Void,
updateIsFocused: @escaping (Bool) -> Void
) {
self.context = context self.context = context
self.source = source self.source = source
self.isPremium = isPremium self.isPremium = isPremium
@ -1211,7 +1225,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
self.otherPeerName = otherPeerName self.otherPeerName = otherPeerName
self.products = products self.products = products
self.selectedProductId = selectedProductId self.selectedProductId = selectedProductId
self.validTransactionIds = validTransactionIds self.validPurchases = validPurchases
self.promoConfiguration = promoConfiguration self.promoConfiguration = promoConfiguration
self.present = present self.present = present
self.selectProduct = selectProduct self.selectProduct = selectProduct
@ -1241,7 +1255,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
if lhs.selectedProductId != rhs.selectedProductId { if lhs.selectedProductId != rhs.selectedProductId {
return false return false
} }
if lhs.validTransactionIds != rhs.validTransactionIds { if lhs.validPurchases != rhs.validPurchases {
return false return false
} }
if lhs.promoConfiguration != rhs.promoConfiguration { if lhs.promoConfiguration != rhs.promoConfiguration {
@ -1256,7 +1270,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
var products: [PremiumProduct]? var products: [PremiumProduct]?
var selectedProductId: String? var selectedProductId: String?
var validTransactionIds: [String] = [] var validPurchases: [InAppPurchaseManager.ReceiptPurchase] = []
var isPremium: Bool? var isPremium: Bool?
@ -1276,7 +1290,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
var canUpgrade: Bool { var canUpgrade: Bool {
if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId { if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId {
if self.validTransactionIds.contains(transactionId) { if self.validPurchases.contains(where: { $0.transactionId == transactionId }) {
return products.first(where: { $0.months > current.months }) != nil return products.first(where: { $0.months > current.months }) != nil
} else { } else {
return false return false
@ -1373,7 +1387,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
let state = context.state let state = context.state
state.products = context.component.products state.products = context.component.products
state.selectedProductId = context.component.selectedProductId state.selectedProductId = context.component.selectedProductId
state.validTransactionIds = context.component.validTransactionIds state.validPurchases = context.component.validPurchases
state.isPremium = context.component.isPremium state.isPremium = context.component.isPremium
let theme = environment.theme let theme = environment.theme
@ -1986,7 +2000,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
private(set) var products: [PremiumProduct]? private(set) var products: [PremiumProduct]?
private(set) var selectedProductId: String? private(set) var selectedProductId: String?
fileprivate var validTransactionIds: [String] = [] fileprivate var validPurchases: [InAppPurchaseManager.ReceiptPurchase] = []
var isPremium: Bool? var isPremium: Bool?
var otherPeerName: String? var otherPeerName: String?
@ -2015,7 +2029,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
var canUpgrade: Bool { var canUpgrade: Bool {
if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId { if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId {
if self.validTransactionIds.contains(transactionId) { if self.validPurchases.contains(where: { $0.transactionId == transactionId }) {
return products.first(where: { $0.months > current.months }) != nil return products.first(where: { $0.months > current.months }) != nil
} else { } else {
return false return false
@ -2036,7 +2050,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
super.init() super.init()
self.validTransactionIds = context.inAppPurchaseManager?.getValidTransactionIds() ?? [] self.validPurchases = context.inAppPurchaseManager?.getReceiptPurchases() ?? []
let availableProducts: Signal<[InAppPurchaseManager.Product], NoError> let availableProducts: Signal<[InAppPurchaseManager.Product], NoError>
if let inAppPurchaseManager = context.inAppPurchaseManager { if let inAppPurchaseManager = context.inAppPurchaseManager {
@ -2136,7 +2150,28 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
let premiumProduct = self.products?.first(where: { $0.id == self.selectedProductId }), !self.inProgress else { let premiumProduct = self.products?.first(where: { $0.id == self.selectedProductId }), !self.inProgress else {
return return
} }
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let isUpgrade = self.products?.first(where: { $0.isCurrent }) != nil let isUpgrade = self.products?.first(where: { $0.isCurrent }) != nil
var hasActiveSubsciption = false
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_receipt_check"] {
} else if !self.validPurchases.isEmpty && !isUpgrade {
let now = Date()
for purchase in self.validPurchases.reversed() {
if (purchase.productId.hasSuffix(".monthly") || purchase.productId.hasSuffix(".annual")) && purchase.expirationDate > now {
hasActiveSubsciption = true
}
}
}
if hasActiveSubsciption {
let errorText = presentationData.strings.Premium_Purchase_OnlyOneSubscriptionAllowed
let alertController = textAlertController(context: self.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
self.present(alertController)
return
}
addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept") addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
@ -2173,7 +2208,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
strongSelf.present(alertController) strongSelf.present(alertController)
@ -2198,7 +2232,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
strongSelf.updateInProgress(false) strongSelf.updateInProgress(false)
strongSelf.updated(transition: .immediate) strongSelf.updated(transition: .immediate)
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
var errorText: String? var errorText: String?
switch error { switch error {
case .generic: case .generic:
@ -2475,7 +2508,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
otherPeerName: state.otherPeerName, otherPeerName: state.otherPeerName,
products: state.products, products: state.products,
selectedProductId: state.selectedProductId, selectedProductId: state.selectedProductId,
validTransactionIds: state.validTransactionIds, validPurchases: state.validPurchases,
promoConfiguration: state.promoConfiguration, promoConfiguration: state.promoConfiguration,
present: context.component.present, present: context.component.present,
selectProduct: { [weak state] productId in selectProduct: { [weak state] productId in

View File

@ -188,7 +188,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
case .x0_5: case .x0_5:
self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor)))
case .x1: case .x1:
self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.setContent(.image(optionsRateImage(rate: "1X", color: self.theme.rootController.navigationBar.controlColor)))
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
@ -378,7 +378,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
case .x0_5: case .x0_5:
self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor)))
case .x1: case .x1:
self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.setContent(.image(optionsRateImage(rate: "1X", color: self.theme.rootController.navigationBar.controlColor)))
case .x1_5: case .x1_5:
self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor)))
case .x2: case .x2:
@ -511,6 +511,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
if let rate = self.playbackBaseRate { if let rate = self.playbackBaseRate {
switch rate { switch rate {
case .x1: case .x1:
nextRate = .x1_5
case .x1_5:
nextRate = .x2 nextRate = .x2
default: default:
nextRate = .x1 nextRate = .x1

View File

@ -687,41 +687,41 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
} }
strongSelf.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type) strongSelf.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type)
var hasTooltip = false // var hasTooltip = false
strongSelf.forEachController({ controller in // strongSelf.forEachController({ controller in
if let controller = controller as? UndoOverlayController { // if let controller = controller as? UndoOverlayController {
hasTooltip = true // hasTooltip = true
controller.dismissWithCommitAction() // controller.dismissWithCommitAction()
} // }
return true // return true
}) // })
//
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } // let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let slowdown: Bool? // let slowdown: Bool?
if baseRate == .x1 { // if baseRate == .x1 {
slowdown = true // slowdown = true
} else if baseRate == .x2 { // } else if baseRate == .x2 {
slowdown = false // slowdown = false
} else { // } else {
slowdown = nil // slowdown = nil
} // }
if let slowdown = slowdown { // if let slowdown = slowdown {
strongSelf.present( // strongSelf.present(
UndoOverlayController( // UndoOverlayController(
presentationData: presentationData, // presentationData: presentationData,
content: .audioRate( // content: .audioRate(
slowdown: slowdown, // slowdown: slowdown,
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp // text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
), // ),
elevatedLayout: false, // elevatedLayout: false,
animateInAsReplacement: hasTooltip, // animateInAsReplacement: hasTooltip,
action: { action in // action: { action in
return true // return true
} // }
), // ),
in: .current // in: .current
) // )
} // }
}) })
} }
mediaAccessoryPanel.togglePlayPause = { [weak self] in mediaAccessoryPanel.togglePlayPause = { [weak self] in

View File

@ -35,6 +35,47 @@ private func generateCollapseIcon(theme: PresentationTheme) -> UIImage? {
}) })
} }
private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 16.0), rotatedContext: { size, context in
UIGraphicsPushContext(context)
context.clear(CGRect(origin: CGPoint(), size: size))
let lineWidth = 1.0 + UIScreenPixel
context.setLineWidth(lineWidth)
context.setStrokeColor(color.cgColor)
let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .bold), textColor: color)
var offset = CGPoint(x: 1.0, y: 0.0)
var width: CGFloat
if rate.count >= 3 {
if rate == "0.5X" {
string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string))
offset.x += -0.5
} else {
string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string))
offset.x += -0.3
}
width = 29.0
} else {
string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string))
width = 19.0
offset.x += -0.3
}
let path = UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - width) / 2.0), y: 0.0, width: width, height: 16.0).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0))
context.addPath(path.cgPath)
context.strokePath()
let boundingRect = string.boundingRect(with: size, options: [], context: nil)
string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + UIScreenPixel + floor((size.height - boundingRect.height) / 2.0)))
UIGraphicsPopContext()
})
}
private let digitsSet = CharacterSet(charactersIn: "0123456789") private let digitsSet = CharacterSet(charactersIn: "0123456789")
private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat { private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat {
let text: String let text: String
@ -375,8 +416,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
} }
let baseRate: AudioPlaybackRate let baseRate: AudioPlaybackRate
if !value.status.baseRate.isEqual(to: 1.0) { if value.status.baseRate.isEqual(to: 2.0) {
baseRate = .x2 baseRate = .x2
} else if value.status.baseRate.isEqual(to: 1.5) {
baseRate = .x1_5
} else { } else {
baseRate = .x1 baseRate = .x1
} }
@ -715,10 +758,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
} }
private func updateOrderButton(_ order: MusicPlaybackSettingsOrder) { private func updateOrderButton(_ order: MusicPlaybackSettingsOrder) {
let baseColor = self.presentationData.theme.list.itemSecondaryTextColor
switch order { switch order {
case .regular: case .regular:
self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: baseColor) self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.presentationData.theme.list.itemSecondaryTextColor)
case .reversed: case .reversed:
self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.presentationData.theme.list.itemAccentColor) self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.presentationData.theme.list.itemAccentColor)
case .random: case .random:
@ -740,10 +782,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
private func updateRateButton(_ baseRate: AudioPlaybackRate) { private func updateRateButton(_ baseRate: AudioPlaybackRate) {
switch baseRate { switch baseRate {
case .x2: case .x2:
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerMaximizedRateActiveIcon(self.presentationData.theme), for: []) self.rateButton.setImage(optionsRateImage(rate: "2X", color: self.presentationData.theme.list.itemAccentColor), for: [])
default: case .x1_5:
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerMaximizedRateInactiveIcon(self.presentationData.theme), for: []) self.rateButton.setImage(optionsRateImage(rate: "1.5X", color: self.presentationData.theme.list.itemAccentColor), for: [])
default:
self.rateButton.setImage(optionsRateImage(rate: "1X", color: self.presentationData.theme.list.itemSecondaryTextColor), for: [])
} }
} }
@ -959,12 +1003,14 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
if let currentRate = self.currentRate { if let currentRate = self.currentRate {
switch currentRate { switch currentRate {
case .x1: case .x1:
nextRate = .x1_5
case .x1_5:
nextRate = .x2 nextRate = .x2
default: default:
nextRate = .x1 nextRate = .x1
} }
} else { } else {
nextRate = .x2 nextRate = .x1_5
} }
self.control?(.setBaseRate(nextRate)) self.control?(.setBaseRate(nextRate))
} }