Merge commit 'f1753011e1e5ca79e4720288fd7a521344ef6373'

This commit is contained in:
Ali 2022-07-25 15:32:11 +02:00
commit 235482fea1
35 changed files with 473 additions and 340 deletions

Binary file not shown.

Binary file not shown.

View File

@ -7869,6 +7869,7 @@ Sorry for the inconvenience.";
"Premium.AnimatedEmoji" = "Animated Emoji"; "Premium.AnimatedEmoji" = "Animated Emoji";
"Premium.AnimatedEmojiInfo" = "Include animated emoji from different emoji sets in any message you send."; "Premium.AnimatedEmojiInfo" = "Include animated emoji from different emoji sets in any message you send.";
"Premium.AnimatedEmojiStandaloneInfo" = "Include animated emoji from different emoji sets in any message you send.";
"ChatContextMenu.EmojiSetSingle" = "This message contains\n#[%@]() emoji."; "ChatContextMenu.EmojiSetSingle" = "This message contains\n#[%@]() emoji.";
"ChatContextMenu.EmojiSet_1" = "This message contains emoji from [%@ pack]()."; "ChatContextMenu.EmojiSet_1" = "This message contains emoji from [%@ pack]().";
@ -7886,6 +7887,12 @@ Sorry for the inconvenience.";
"EmojiPackActionInfo.ArchivedTitle" = "Emoji Archived"; "EmojiPackActionInfo.ArchivedTitle" = "Emoji Archived";
"EmojiPackActionInfo.RemovedText" = "%@ is no longer in your emoji."; "EmojiPackActionInfo.RemovedText" = "%@ is no longer in your emoji.";
"EmojiPackActionInfo.MultipleAddedText_1" = "%@ emoji pack has been added to your emoji.";
"EmojiPackActionInfo.MultipleAddedText_any" = "%@ emoji packs have been added to your emoji.";
"EmojiPackActionInfo.MultipleRemovedText_1" = "%@ emoji pack is no longer in your emoji.";
"EmojiPackActionInfo.MultipleRemovedText_any" = "%@ emoji packs are no longer in your emoji.";
"MaskPackActionInfo.RemovedTitle" = "Masks Removed"; "MaskPackActionInfo.RemovedTitle" = "Masks Removed";
"MaskPackActionInfo.ArchivedTitle" = "Masks Archived"; "MaskPackActionInfo.ArchivedTitle" = "Masks Archived";
"MaskPackActionInfo.RemovedText" = "%@ is no longer in your masks."; "MaskPackActionInfo.RemovedText" = "%@ is no longer in your masks.";
@ -7911,6 +7918,9 @@ Sorry for the inconvenience.";
"StickerSettings.EmojiContextInfo" = "If you archive an emoji set, you can quickly restore it later from the Archived Emoji section."; "StickerSettings.EmojiContextInfo" = "If you archive an emoji set, you can quickly restore it later from the Archived Emoji section.";
"StickerPack.CopyLinks" = "Copy Links";
"Conversation.LinksCopied" = "Links copied to clipboard";
"StickersList.EmojiItem" = "Custom Emoji"; "StickersList.EmojiItem" = "Custom Emoji";
"StickersList.ArchivedEmojiItem" = "Archived Emoji"; "StickersList.ArchivedEmojiItem" = "Archived Emoji";

View File

@ -560,6 +560,8 @@ public protocol ChatController: ViewController {
func beginMessageSearch(_ query: String) func beginMessageSearch(_ query: String)
func displayPromoAnnouncement(text: String) func displayPromoAnnouncement(text: String)
func hintPlayNextOutgoingGift()
var isSendButtonVisible: Bool { get } var isSendButtonVisible: Bool { get }
} }

View File

@ -481,11 +481,13 @@ private func availablePaymentMethods(form: BotPaymentForm, current: BotCheckoutP
methods.append(.applePay) methods.append(.applePay)
hasApplePay = true hasApplePay = true
} }
if let savedCredentials = form.savedCredentials {
for savedCredentials in form.savedCredentials {
if !methods.contains(.savedCredentials(savedCredentials)) { if !methods.contains(.savedCredentials(savedCredentials)) {
methods.append(.savedCredentials(savedCredentials)) methods.append(.savedCredentials(savedCredentials))
} }
} }
if !form.additionalPaymentMethods.isEmpty { if !form.additionalPaymentMethods.isEmpty {
methods.append(contentsOf: form.additionalPaymentMethods.map { .other($0) }) methods.append(contentsOf: form.additionalPaymentMethods.map { .other($0) })
} }
@ -835,7 +837,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} }
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing let canSave = customUrl == nil && (paymentForm.canSaveCredentials || paymentForm.passwordMissing)
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .stripe(additionalFields: additionalFields, publishableKey: publishableKey), completion: { method in let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .stripe(additionalFields: additionalFields, publishableKey: publishableKey), completion: { method in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -1106,7 +1108,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
strongSelf.botPeerValue = formAndValidatedInfo.botPeer strongSelf.botPeerValue = formAndValidatedInfo.botPeer
strongSelf.currentFormInfo = savedInfo strongSelf.currentFormInfo = savedInfo
strongSelf.currentValidatedFormInfo = formAndValidatedInfo.validatedFormInfo strongSelf.currentValidatedFormInfo = formAndValidatedInfo.validatedFormInfo
if let savedCredentials = formAndValidatedInfo.form.savedCredentials { if let savedCredentials = formAndValidatedInfo.form.savedCredentials.first {
strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials) strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials)
} }
strongSelf.actionButton.isEnabled = true strongSelf.actionButton.isEnabled = true

View File

@ -471,7 +471,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
if self.animateInputField { if self.animateInputField {
self.fromMessageTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) self.fromMessageTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
self.toMessageTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, removeOnCompletion: false) self.toMessageTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, removeOnCompletion: false)
self.textInputNode.isHidden = true
} else { } else {
self.messageBackgroundNode.isHidden = true self.messageBackgroundNode.isHidden = true
self.fromMessageTextNode.isHidden = true self.fromMessageTextNode.isHidden = true
@ -526,6 +525,9 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
Queue.mainQueue().after(0.01, { Queue.mainQueue().after(0.01, {
if self.animateInputField {
self.textInputNode.isHidden = true
}
self.updateTextContents() self.updateTextContents()
}) })
} }

View File

@ -662,15 +662,18 @@ public class Window1 {
return return
} }
self.forceBadgeHidden = hidden self.forceBadgeHidden = hidden
self.updateBadgeVisibility(layout: self.windowLayout) self.updateBadgeVisibility()
} }
private func updateBadgeVisibility(layout: WindowLayout) { private func updateBadgeVisibility() {
let badgeIsHidden = !self.deviceMetrics.hasTopNotch || self.forceBadgeHidden || layout.size.width > layout.size.height let badgeIsHidden = !self.deviceMetrics.hasTopNotch || self.forceBadgeHidden || self.windowLayout.size.width > self.windowLayout.size.height
if badgeIsHidden != self.badgeView.isHidden && !badgeIsHidden { if badgeIsHidden != self.badgeView.isHidden && !badgeIsHidden {
Queue.mainQueue().after(0.3) { Queue.mainQueue().after(0.4) {
let badgeShouldBeHidden = !self.deviceMetrics.hasTopNotch || self.forceBadgeHidden || self.windowLayout.size.width > self.windowLayout.size.height
if badgeShouldBeHidden == badgeIsHidden {
self.badgeView.isHidden = badgeIsHidden self.badgeView.isHidden = badgeIsHidden
} }
}
} else { } else {
self.badgeView.isHidden = badgeIsHidden self.badgeView.isHidden = badgeIsHidden
} }
@ -1115,7 +1118,7 @@ public class Window1 {
} }
if let image = self.badgeView.image { if let image = self.badgeView.image {
self.updateBadgeVisibility(layout: self.windowLayout) self.updateBadgeVisibility()
self.badgeView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.windowLayout.size.width - image.size.width) / 2.0), y: 6.0), size: image.size) self.badgeView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.windowLayout.size.width - image.size.width) / 2.0), y: 6.0), size: image.size)
} }
} }

View File

@ -474,7 +474,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
} }
let baseNavigationController = strongSelf.baseNavigationController() let baseNavigationController = strongSelf.baseNavigationController()
baseNavigationController?.view.endEditing(true) baseNavigationController?.view.endEditing(true)
let controller = StickerPackScreen(context: context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in let controller = StickerPackScreen(context: context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { actions in
if let (info, items, action) = actions.first {
let animateInAsReplacement = false let animateInAsReplacement = false
switch action { switch action {
case .add: case .add:
@ -489,6 +490,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
return true return true
}), in: .window(.root)) }), in: .window(.root))
} }
}
}) })
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil) (baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
}) })

View File

@ -2477,15 +2477,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil) c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
}))) })))
if #available(iOS 11.0, *) { // if #available(iOS 11.0, *) {
items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in // items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
f(.default) // f(.default)
guard let strongSelf = self else { // guard let strongSelf = self else {
return // return
} // }
strongSelf.beginAirPlaySetup() // strongSelf.beginAirPlaySetup()
}))) // })))
} // }
if let (message, _, _) = strongSelf.contentInfo() { if let (message, _, _) = strongSelf.contentInfo() {
for media in message.media { for media in message.media {
@ -2667,7 +2667,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
} }
let baseNavigationController = strongSelf.baseNavigationController() let baseNavigationController = strongSelf.baseNavigationController()
baseNavigationController?.view.endEditing(true) baseNavigationController?.view.endEditing(true)
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: Array(packs.prefix(1)), sendSticker: nil, actionPerformed: { info, items, action in let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: Array(packs.prefix(1)), sendSticker: nil, actionPerformed: { actions in
if let (info, items, action) = actions.first {
let animateInAsReplacement = false let animateInAsReplacement = false
switch action { switch action {
case .add: case .add:
@ -2682,6 +2683,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
return true return true
}), in: .window(.root)) }), in: .window(.root))
} }
}
}, dismissed: { [weak self] in }, dismissed: { [weak self] in
self?.isInteractingPromise.set(false) self?.isInteractingPromise.set(false)
}) })

View File

@ -694,7 +694,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
} }
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: firstItem ?? items.first, context: strongSelf.context), elevatedLayout: false, action: { action in strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: firstItem ?? items.first, context: strongSelf.context), elevatedLayout: false, action: { action in
if case .info = action { if case .info = action {
(navigationController?.viewControllers.last as? ViewController)?.present(StickerPackScreen(context: context, mode: .settings, mainStickerPack: .id(id: info.id.id, accessHash: info.accessHash), stickerPacks: [], parentNavigationController: navigationController, actionPerformed: { _, _, _ in (navigationController?.viewControllers.last as? ViewController)?.present(StickerPackScreen(context: context, mode: .settings, mainStickerPack: .id(id: info.id.id, accessHash: info.accessHash), stickerPacks: [], parentNavigationController: navigationController, actionPerformed: { _ in
}), in: .window(.root)) }), in: .window(.root))
} }
return true return true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -909,7 +909,7 @@ private final class DemoSheetContent: CombinedComponent {
decoration: .emoji decoration: .emoji
)), )),
title: strings.Premium_AnimatedEmoji, title: strings.Premium_AnimatedEmoji,
text: strings.Premium_AnimatedEmojiInfo, text: isStandalone ? strings.Premium_AnimatedEmojiStandaloneInfo : strings.Premium_AnimatedEmojiInfo,
textColor: textColor textColor: textColor
) )
) )

View File

@ -145,7 +145,7 @@ private final class ProductGroupComponent: Component {
buttonView?.backgroundColor = component.selectionColor buttonView?.backgroundColor = component.selectionColor
} else { } else {
UIView.animate(withDuration: 0.3, animations: { UIView.animate(withDuration: 0.3, animations: {
buttonView?.backgroundColor = nil buttonView?.backgroundColor = component.backgroundColor
}) })
} }
} }
@ -299,6 +299,8 @@ private final class GiftComponent: CombinedComponent {
transition: context.transition transition: context.transition
) )
let discountSize: CGSize
if !component.discount.isEmpty {
let discount = discount.update( let discount = discount.update(
component: MultilineTextComponent( component: MultilineTextComponent(
text: .plain( text: .plain(
@ -314,7 +316,7 @@ private final class GiftComponent: CombinedComponent {
transition: context.transition transition: context.transition
) )
let discountSize = CGSize(width: discount.size.width + 6.0, height: 18.0) discountSize = CGSize(width: discount.size.width + 6.0, height: 18.0)
let discountBackground = discountBackground.update( let discountBackground = discountBackground.update(
component: RoundedRectangle( component: RoundedRectangle(
@ -325,6 +327,17 @@ private final class GiftComponent: CombinedComponent {
transition: context.transition transition: context.transition
) )
context.add(discountBackground
.position(CGPoint(x: insets.left + discountSize.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0))
)
context.add(discount
.position(CGPoint(x: insets.left + discountSize.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0))
)
} else {
discountSize = CGSize(width: 0.0, height: 18.0)
}
let subtitle = subtitle.update( let subtitle = subtitle.update(
component: MultilineTextComponent( component: MultilineTextComponent(
text: .plain( text: .plain(
@ -360,16 +373,8 @@ private final class GiftComponent: CombinedComponent {
.position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0)) .position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0))
) )
context.add(discountBackground
.position(CGPoint(x: insets.left + discountSize.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0))
)
context.add(discount
.position(CGPoint(x: insets.left + discountSize.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0))
)
context.add(subtitle context.add(subtitle
.position(CGPoint(x: insets.left + discountSize.width + 7.0 + subtitle.size.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0)) .position(CGPoint(x: insets.left + (discountSize.width.isZero ? 0.0 : discountSize.width + 7.0) + subtitle.size.width / 2.0, y: insets.top + title.size.height + spacing + discountSize.height / 2.0))
) )
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitle.size.height + insets.bottom) let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitle.size.height + insets.bottom)
@ -510,13 +515,13 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
let context: AccountContext let context: AccountContext
let peer: EnginePeer? let peer: EnginePeer?
let products: [InAppPurchaseManager.Product]? let products: [PremiumGiftProduct]?
let selectedProductId: String? let selectedProductId: String?
let present: (ViewController) -> Void let present: (ViewController) -> Void
let selectProduct: (String) -> Void let selectProduct: (String) -> Void
init(context: AccountContext, peer: EnginePeer?, products: [InAppPurchaseManager.Product]?, selectedProductId: String?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void) { init(context: AccountContext, peer: EnginePeer?, products: [PremiumGiftProduct]?, selectedProductId: String?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void) {
self.context = context self.context = context
self.peer = peer self.peer = peer
self.products = products self.products = products
@ -629,26 +634,26 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
var i = 0 var i = 0
if let products = component.products { if let products = component.products {
let shortestOptionPrice: Int64
if let product = products.last {
shortestOptionPrice = Int64(Float(product.storeProduct.priceCurrencyAndAmount.amount) / Float(product.months))
} else {
shortestOptionPrice = 1
}
for product in products { for product in products {
let monthsCount: Int
let giftTitle: String let giftTitle: String
let discount: String if product.months == 12 {
switch product.id {
case "org.telegram.telegramPremium.twelveMonths":
giftTitle = strings.Premium_Gift_Years(1) giftTitle = strings.Premium_Gift_Years(1)
monthsCount = 12 } else {
discount = "-15%" giftTitle = strings.Premium_Gift_Months(product.months)
case "org.telegram.telegramPremium.sixMonths": }
giftTitle = strings.Premium_Gift_Months(6)
monthsCount = 6 let discountValue = Int((1.0 - Float(product.storeProduct.priceCurrencyAndAmount.amount) / Float(product.months) / Float(shortestOptionPrice)) * 100.0)
discount = "-10%" let discount: String
case "org.telegram.telegramPremium.threeMonths": if discountValue > 0 {
giftTitle = strings.Premium_Gift_Months(3) discount = "-\(discountValue)%"
monthsCount = 3 } else {
discount = "-7%"
default:
giftTitle = ""
monthsCount = 1
discount = "" discount = ""
} }
@ -659,7 +664,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
GiftComponent( GiftComponent(
title: giftTitle, title: giftTitle,
totalPrice: product.price, totalPrice: product.price,
perMonthPrice: strings.Premium_Gift_PricePerMonth(product.pricePerMonth(monthsCount)).string, perMonthPrice: strings.Premium_Gift_PricePerMonth(product.pricePerMonth).string,
discount: discount, discount: discount,
selected: product.id == component.selectedProductId, selected: product.id == component.selectedProductId,
primaryTextColor: textColor, primaryTextColor: textColor,
@ -705,19 +710,42 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
} }
} }
private struct PremiumGiftProduct: Equatable {
let giftOption: CachedPremiumGiftOption
let storeProduct: InAppPurchaseManager.Product
var id: String {
return self.storeProduct.id
}
var months: Int32 {
return self.giftOption.months
}
var price: String {
return self.storeProduct.price
}
var pricePerMonth: String {
return self.storeProduct.pricePerMonth(Int(self.months))
}
}
private final class PremiumGiftScreenComponent: CombinedComponent { private final class PremiumGiftScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext let context: AccountContext
let peerId: PeerId let peerId: PeerId
let options: [CachedPremiumGiftOption]
let updateInProgress: (Bool) -> Void let updateInProgress: (Bool) -> Void
let present: (ViewController) -> Void let present: (ViewController) -> Void
let push: (ViewController) -> Void let push: (ViewController) -> Void
let completion: (Int32) -> Void let completion: (Int32) -> Void
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) { init(context: AccountContext, peerId: PeerId, options: [CachedPremiumGiftOption], updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.options = options
self.updateInProgress = updateInProgress self.updateInProgress = updateInProgress
self.present = present self.present = present
self.push = push self.push = push
@ -731,12 +759,16 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
if lhs.peerId != rhs.peerId { if lhs.peerId != rhs.peerId {
return false return false
} }
if lhs.options != rhs.options {
return false
}
return true return true
} }
final class State: ComponentState { final class State: ComponentState {
private let context: AccountContext private let context: AccountContext
private let peerId: PeerId private let peerId: PeerId
private let options: [CachedPremiumGiftOption]
private let updateInProgress: (Bool) -> Void private let updateInProgress: (Bool) -> Void
private let present: (ViewController) -> Void private let present: (ViewController) -> Void
private let completion: (Int32) -> Void private let completion: (Int32) -> Void
@ -749,16 +781,17 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
var inProgress = false var inProgress = false
var peer: EnginePeer? var peer: EnginePeer?
var products: [InAppPurchaseManager.Product]? var products: [PremiumGiftProduct]?
var selectedProductId: String? var selectedProductId: String?
private var disposable: Disposable? private var disposable: Disposable?
private var paymentDisposable = MetaDisposable() private var paymentDisposable = MetaDisposable()
private var activationDisposable = MetaDisposable() private var activationDisposable = MetaDisposable()
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) { init(context: AccountContext, peerId: PeerId, options: [CachedPremiumGiftOption], updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.options = options
self.updateInProgress = updateInProgress self.updateInProgress = updateInProgress
self.present = present self.present = present
self.completion = completion self.completion = completion
@ -778,8 +811,18 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
).start(next: { [weak self] products, peer in ).start(next: { [weak self] products, peer in
if let strongSelf = self { if let strongSelf = self {
strongSelf.products = products.filter { !$0.isSubscription }.sorted(by: { $0.priceValue.compare($1.priceValue) == .orderedDescending })
var gifts: [PremiumGiftProduct] = []
for option in strongSelf.options {
if let product = products.first(where: { $0.id == option.storeProductId }), !product.isSubscription {
gifts.append(PremiumGiftProduct(giftOption: option, storeProduct: product))
}
}
strongSelf.products = gifts
if strongSelf.selectedProductId == nil {
strongSelf.selectedProductId = strongSelf.products?.first?.id strongSelf.selectedProductId = strongSelf.products?.first?.id
}
strongSelf.peer = peer strongSelf.peer = peer
strongSelf.updated(transition: .immediate) strongSelf.updated(transition: .immediate)
} }
@ -805,19 +848,8 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
guard let product = self.products?.first(where: { $0.id == self.selectedProductId }) else { guard let product = self.products?.first(where: { $0.id == self.selectedProductId }) else {
return return
} }
let (currency, amount) = product.priceCurrencyAndAmount let (currency, amount) = product.storeProduct.priceCurrencyAndAmount
let duration = product.months
let duration: Int32
switch product.id {
case "org.telegram.telegramPremium.twelveMonths":
duration = 12
case "org.telegram.telegramPremium.sixMonths":
duration = 6
case "org.telegram.telegramPremium.threeMonths":
duration = 3
default:
duration = 0
}
// addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept") // addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
@ -829,7 +861,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|> deliverOnMainQueue).start(next: { [weak self] available in |> deliverOnMainQueue).start(next: { [weak self] available in
if let strongSelf = self { if let strongSelf = self {
if available { if available {
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(product, targetPeerId: strongSelf.peerId) strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(product.storeProduct, targetPeerId: strongSelf.peerId)
|> deliverOnMainQueue).start(next: { [weak self] status in |> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self, case .purchased = status { if let strongSelf = self, case .purchased = status {
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId) strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
@ -852,7 +884,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
strongSelf.updated(transition: .immediate) strongSelf.updated(transition: .immediate)
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 presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
@ -896,7 +928,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
} }
if let errorText = errorText { if let errorText = errorText {
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") // addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
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)
@ -919,7 +951,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
} }
func makeState() -> State { func makeState() -> State {
return State(context: self.context, peerId: self.peerId, updateInProgress: self.updateInProgress, present: self.present, completion: self.completion) return State(context: self.context, peerId: self.peerId, options: self.options, updateInProgress: self.updateInProgress, present: self.present, completion: self.completion)
} }
static var body: Body { static var body: Body {
@ -1242,6 +1274,7 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
super.init(context: context, component: PremiumGiftScreenComponent( super.init(context: context, component: PremiumGiftScreenComponent(
context: context, context: context,
peerId: peerId, peerId: peerId,
options: options,
updateInProgress: { inProgress in updateInProgress: { inProgress in
updateInProgressImpl?(inProgress) updateInProgressImpl?(inProgress)
}, },
@ -1278,6 +1311,12 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers var controllers = navigationController.viewControllers
controllers = controllers.filter { !($0 is PeerInfoScreen) && !($0 is PremiumGiftScreen) } controllers = controllers.filter { !($0 is PeerInfoScreen) && !($0 is PremiumGiftScreen) }
for controller in controllers.reversed() {
if let chatController = controller as? ChatController, case .peer(id: peerId) = chatController.chatLocation {
chatController.hintPlayNextOutgoingGift()
break
}
}
navigationController.setViewControllers(controllers, animated: true) navigationController.setViewControllers(controllers, animated: true)
} }
} }

View File

@ -300,7 +300,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
case let .privacyHeader(_, text): case let .privacyHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .blockedPeers(_, text, value): case let .blockedPeers(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Chat/Stickers/Blocked")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Blocked")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openBlockedUsers() arguments.openBlockedUsers()
}) })
case let .phoneNumberPrivacy(_, text, value): case let .phoneNumberPrivacy(_, text, value):

View File

@ -1267,7 +1267,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
packs.insert(packReference, at: 0) packs.insert(packReference, at: 0)
} }
if let mainStickerPack = mainStickerPack { if let mainStickerPack = mainStickerPack {
presentControllerImpl?(StickerPackScreen(context: context, mode: .settings, mainStickerPack: mainStickerPack, stickerPacks: [mainStickerPack], parentNavigationController: controller?.navigationController as? NavigationController, actionPerformed: { info, items, action in presentControllerImpl?(StickerPackScreen(context: context, mode: .settings, mainStickerPack: mainStickerPack, stickerPacks: [mainStickerPack], parentNavigationController: controller?.navigationController as? NavigationController, actionPerformed: { actions in
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var animateInAsReplacement = false var animateInAsReplacement = false
if let navigationController = navigationControllerImpl?() { if let navigationController = navigationControllerImpl?() {
@ -1278,6 +1278,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
} }
} }
} }
if let (info, items, action) = actions.first {
switch action { switch action {
case .add: case .add:
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in
@ -1304,6 +1305,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
return true return true
})) }))
} }
}
}), nil) }), nil)
} }
}) })

View File

@ -192,7 +192,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
lockBackground.isUserInteractionEnabled = false lockBackground.isUserInteractionEnabled = false
lockIconNode = ASImageNode() lockIconNode = ASImageNode()
lockIconNode.displaysAsynchronously = false lockIconNode.displaysAsynchronously = false
lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: .white) lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
let lockTintView = UIView() let lockTintView = UIView()
lockTintView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15) lockTintView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15)
@ -321,8 +321,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
} }
if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode { if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
let lockSize = CGSize(width: 30.0, height: 30.0) let lockSize = CGSize(width: 16.0, height: 16.0)
let lockBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - lockSize.width) / 2.0), y: bounds.height - lockSize.height - 6.0), size: lockSize) let lockBackgroundFrame = CGRect(origin: CGPoint(x: bounds.width - lockSize.width, y: bounds.height - lockSize.height), size: lockSize)
lockBackground.frame = lockBackgroundFrame lockBackground.frame = lockBackgroundFrame
lockBackground.layer.cornerRadius = lockSize.width / 2.0 lockBackground.layer.cornerRadius = lockSize.width / 2.0
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
@ -330,7 +330,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
} }
lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size) lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size)
if let icon = lockIconNode.image { if let icon = lockIconNode.image {
lockIconNode.frame = CGRect(origin: CGPoint(x: lockBackgroundFrame.minX + floorToScreenPixels((lockBackgroundFrame.width - icon.size.width) / 2.0), y: lockBackgroundFrame.minY + floorToScreenPixels((lockBackgroundFrame.height - icon.size.height) / 2.0)), size: icon.size) let iconSize = CGSize(width: icon.size.width - 4.0, height: icon.size.height - 4.0)
lockIconNode.frame = CGRect(origin: CGPoint(x: lockBackgroundFrame.minX + floorToScreenPixels((lockBackgroundFrame.width - iconSize.width) / 2.0), y: lockBackgroundFrame.minY + floorToScreenPixels((lockBackgroundFrame.height - iconSize.height) / 2.0)), size: iconSize)
} }
} }
} }

View File

@ -586,7 +586,10 @@ private final class StickerPackContainer: ASDisplayNode {
strongSelf.controller?.present(shareController, in: .window(.root)) strongSelf.controller?.present(shareController, in: .window(.root))
} }
}))) })))
items.append(.action(ContextMenuActionItem(text: strings.StickerPack_CopyLink, icon: { theme in
let copyTitle = self.currentStickerPacks.count > 1 ? strings.StickerPack_CopyLinks : strings.StickerPack_CopyLink
let copyText = self.currentStickerPacks.count > 1 ? strings.Conversation_LinksCopied : strings.Conversation_LinkCopied
items.append(.action(ContextMenuActionItem(text: copyTitle, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.default) f(.default)
@ -595,7 +598,7 @@ private final class StickerPackContainer: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: copyText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
} }
}))) })))
@ -629,14 +632,15 @@ private final class StickerPackContainer: ASDisplayNode {
}) })
} }
} else { } else {
var installedPacks: [(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)] = []
for (info, items, isInstalled) in self.currentStickerPacks { for (info, items, isInstalled) in self.currentStickerPacks {
if !isInstalled { if !isInstalled {
installedPacks.append((info, items, .add))
let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start() let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
// if dismissed {
// actionPerformed?(info, items, .add)
// }
} }
} }
self.controller?.actionPerformed?(installedPacks)
} }
self.requestDismiss() self.requestDismiss()
} else if let (info, items, installed) = self.currentStickerPack { } else if let (info, items, installed) = self.currentStickerPack {
@ -657,13 +661,13 @@ private final class StickerPackContainer: ASDisplayNode {
return return
} }
if dismissed { if dismissed {
actionPerformed?(info, items, .remove(positionInList: positionInList)) actionPerformed?([(info, items, .remove(positionInList: positionInList))])
} }
}) })
} else { } else {
let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start() let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
if dismissed { if dismissed {
actionPerformed?(info, items, .add) actionPerformed?([(info, items, .add)])
} }
} }
} else { } else {
@ -1217,7 +1221,6 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
super.didLoad() super.didLoad()
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapGesture(_:)))) self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapGesture(_:))))
self.containerContainingNode.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
@ -1235,11 +1238,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
transition.updateFrame(node: self.containerContainingNode, frame: CGRect(origin: CGPoint(), size: layout.size)) transition.updateFrame(node: self.containerContainingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
let expandProgress: CGFloat = 1.0 let expandProgress: CGFloat = 1.0
// if self.stickerPacks.count == 1 {
// expandProgress = 1.0
// } else {
// expandProgress = self.containers[self.selectedStickerPackIndex]?.expandProgress ?? 0.0
// }
let scaledInset: CGFloat = 12.0 let scaledInset: CGFloat = 12.0
let scaledDistance: CGFloat = 4.0 let scaledDistance: CGFloat = 4.0
let minScale = (layout.size.width - scaledInset * 2.0) / layout.size.width let minScale = (layout.size.width - scaledInset * 2.0) / layout.size.width
@ -1247,7 +1246,6 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
let containerVerticalOffset: CGFloat = (1.0 - expandProgress) * scaledInset * 2.0 let containerVerticalOffset: CGFloat = (1.0 - expandProgress) * scaledInset * 2.0
// for i in 0 ..< self.stickerPacks.count {
let i = 0 let i = 0
let indexOffset = i - self.selectedStickerPackIndex let indexOffset = i - self.selectedStickerPackIndex
var scaledOffset: CGFloat = 0.0 var scaledOffset: CGFloat = 0.0
@ -1348,7 +1346,6 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
self.containers.removeValue(forKey: i) self.containers.removeValue(forKey: i)
} }
} }
// }
if firstTime { if firstTime {
if !self.containers.isEmpty { if !self.containers.isEmpty {
@ -1512,7 +1509,7 @@ public final class StickerPackScreenImpl: ViewController {
} }
public var dismissed: (() -> Void)? public var dismissed: (() -> Void)?
public var actionPerformed: ((StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction) -> Void)? public var actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)?
private let _ready = Promise<Bool>() private let _ready = Promise<Bool>()
override public var ready: Promise<Bool> { override public var ready: Promise<Bool> {
@ -1527,7 +1524,7 @@ public final class StickerPackScreenImpl: ViewController {
let animationCache: AnimationCache let animationCache: AnimationCache
let animationRenderer: MultiAnimationRenderer let animationRenderer: MultiAnimationRenderer
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, stickerPacks: [StickerPackReference], selectedStickerPackIndex: Int = 0, parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction) -> Void)? = nil) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, stickerPacks: [StickerPackReference], selectedStickerPackIndex: Int = 0, parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil) {
self.context = context self.context = context
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.stickerPacks = stickerPacks self.stickerPacks = stickerPacks
@ -1706,7 +1703,7 @@ public enum StickerPackScreenPerformedAction {
case remove(positionInList: Int) case remove(positionInList: Int)
} }
public func StickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: StickerPackPreviewControllerMode = .default, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction) -> Void)? = nil, dismissed: (() -> Void)? = nil) -> ViewController { public func StickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: StickerPackPreviewControllerMode = .default, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil, dismissed: (() -> Void)? = nil) -> ViewController {
//let stickerPacks = [mainStickerPack] //let stickerPacks = [mainStickerPack]
let controller = StickerPackScreenImpl(context: context, stickerPacks: stickerPacks, selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: actionPerformed) let controller = StickerPackScreenImpl(context: context, stickerPacks: stickerPacks, selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: actionPerformed)
controller.dismissed = dismissed controller.dismissed = dismissed

View File

@ -1006,7 +1006,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) } dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) }
dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) } dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) }
dict[1288001087] = { return Api.payments.PaymentForm.parse_paymentForm($0) } dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) } dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) } dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) } dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }

View File

@ -728,13 +728,13 @@ public extension Api.payments {
} }
public extension Api.payments { public extension Api.payments {
enum PaymentForm: TypeConstructorDescription { enum PaymentForm: TypeConstructorDescription {
case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: Api.PaymentSavedCredentials?, users: [Api.User]) case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users):
if boxed { if boxed {
buffer.appendInt32(1288001087) buffer.appendInt32(-1610250415)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(formId, buffer: buffer, boxed: false) serializeInt64(formId, buffer: buffer, boxed: false)
@ -753,7 +753,11 @@ public extension Api.payments {
item.serialize(buffer, true) item.serialize(buffer, true)
}} }}
if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {savedCredentials!.serialize(buffer, true)} if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(savedCredentials!.count))
for item in savedCredentials! {
item.serialize(buffer, true)
}}
buffer.appendInt32(481674261) buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count)) buffer.appendInt32(Int32(users.count))
for item in users { for item in users {
@ -807,9 +811,9 @@ public extension Api.payments {
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo _13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
} } } }
var _14: Api.PaymentSavedCredentials? var _14: [Api.PaymentSavedCredentials]?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_14 = Api.parse(reader, signature: signature) as? Api.PaymentSavedCredentials _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self)
} } } }
var _15: [Api.User]? var _15: [Api.User]?
if let _ = reader.readInt32() { if let _ = reader.readInt32() {

View File

@ -64,7 +64,7 @@ private final class PendingUpdateMessageManagerImpl {
} }
} }
func add(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool) { func add(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool) {
if let context = self.contexts[messageId] { if let context = self.contexts[messageId] {
self.contexts.removeValue(forKey: messageId) self.contexts.removeValue(forKey: messageId)
context.disposable.dispose() context.disposable.dispose()
@ -163,7 +163,7 @@ public final class PendingUpdateMessageManager {
}) })
} }
public func add(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool = false) { public func add(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool = false) {
self.impl.with { impl in self.impl.with { impl in
impl.add(messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview) impl.add(messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview)
} }

View File

@ -27,11 +27,11 @@ public enum RequestEditMessageError {
case invalidGrouping case invalidGrouping
} }
func _internal_requestEditMessage(account: Account, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool = false, scheduleTime: Int32? = nil) -> Signal<RequestEditMessageResult, RequestEditMessageError> { func _internal_requestEditMessage(account: Account, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool = false, scheduleTime: Int32? = nil) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return requestEditMessage(postbox: account.postbox, network: account.network, stateManager: account.stateManager, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime) return requestEditMessage(postbox: account.postbox, network: account.network, stateManager: account.stateManager, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime)
} }
func requestEditMessage(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool, scheduleTime: Int32?) -> Signal<RequestEditMessageResult, RequestEditMessageError> { func requestEditMessage(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return requestEditMessageInternal(postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: false) return requestEditMessageInternal(postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: false)
|> `catch` { error -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> in |> `catch` { error -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> in
if case .invalidReference = error { if case .invalidReference = error {
@ -50,7 +50,7 @@ func requestEditMessage(postbox: Postbox, network: Network, stateManager: Accoun
} }
} }
private func requestEditMessageInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool, scheduleTime: Int32?, forceReupload: Bool) -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> { private func requestEditMessageInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?, forceReupload: Bool) -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> {
let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError> let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
switch media { switch media {
case .keep: case .keep:

View File

@ -85,7 +85,7 @@ public extension TelegramEngine {
return _internal_clearAuthorHistory(account: self.account, peerId: peerId, memberId: memberId) return _internal_clearAuthorHistory(account: self.account, peerId: peerId, memberId: memberId)
} }
public func requestEditMessage(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: TelegramMediaFile], disableUrlPreview: Bool = false, scheduleTime: Int32? = nil) -> Signal<RequestEditMessageResult, RequestEditMessageError> { public func requestEditMessage(messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool = false, scheduleTime: Int32? = nil) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return _internal_requestEditMessage(account: self.account, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime) return _internal_requestEditMessage(account: self.account, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime)
} }

View File

@ -119,7 +119,7 @@ public struct BotPaymentForm : Equatable {
public let url: String public let url: String
public let nativeProvider: BotPaymentNativeProvider? public let nativeProvider: BotPaymentNativeProvider?
public let savedInfo: BotPaymentRequestedInfo? public let savedInfo: BotPaymentRequestedInfo?
public let savedCredentials: BotPaymentSavedCredentials? public let savedCredentials: [BotPaymentSavedCredentials]
public let additionalPaymentMethods: [BotPaymentMethod] public let additionalPaymentMethods: [BotPaymentMethod]
} }
@ -304,13 +304,12 @@ func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: B
} }
} }
let parsedSavedInfo = savedInfo.flatMap(BotPaymentRequestedInfo.init) let parsedSavedInfo = savedInfo.flatMap(BotPaymentRequestedInfo.init)
var parsedSavedCredentials: BotPaymentSavedCredentials? let parsedSavedCredentials = savedCredentials?.map({ savedCredentials -> BotPaymentSavedCredentials in
if let savedCredentials = savedCredentials {
switch savedCredentials { switch savedCredentials {
case let .paymentSavedCredentialsCard(id, title): case let .paymentSavedCredentialsCard(id, title):
parsedSavedCredentials = .card(id: id, title: title) return .card(id: id, title: title)
}
} }
}) ?? []
let additionalPaymentMethods = additionalMethods?.map({ BotPaymentMethod(apiPaymentFormMethod: $0) }) ?? [] let additionalPaymentMethods = additionalMethods?.map({ BotPaymentMethod(apiPaymentFormMethod: $0) }) ?? []
return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials, additionalPaymentMethods: additionalPaymentMethods) return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials, additionalPaymentMethods: additionalPaymentMethods)

View File

@ -1093,7 +1093,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var disableTransitionAnimations = false var disableTransitionAnimations = false
var actionsSignal: Signal<ContextController.Items, NoError> = .single(actions) var actionsSignal: Signal<ContextController.Items, NoError> = .single(actions)
if actions.tip == nil, let entitiesAttribute = message.textEntitiesAttribute { if let entitiesAttribute = message.textEntitiesAttribute {
var emojiFileIds: [Int64] = [] var emojiFileIds: [Int64] = []
for entity in entitiesAttribute.entities { for entity in entitiesAttribute.entities {
if case let .CustomEmoji(_, fileId) = entity.type { if case let .CustomEmoji(_, fileId) = entity.type {
@ -1128,7 +1128,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
strongSelf.chatDisplayNode.dismissTextInput() strongSelf.chatDisplayNode.dismissTextInput()
let controller = StickerPackScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(packReferences), parentNavigationController: strongSelf.effectiveNavigationController)
let presentationData = strongSelf.presentationData
let controller = StickerPackScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(packReferences), parentNavigationController: strongSelf.effectiveNavigationController, actionPerformed: { [weak self] actions in
guard let strongSelf = self else {
return
}
if actions.count > 1, let first = actions.first {
if case .add = first.2 {
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.EmojiPackActionInfo_AddedTitle, text: presentationData.strings.EmojiPackActionInfo_MultipleAddedText(Int32(actions.count)), undo: false, info: first.0, topItem: first.1.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
return true
}))
}
} else if let (info, items, action) = actions.first {
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
switch action {
case .add:
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedTitle : presentationData.strings.StickerPackActionInfo_AddedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedText(info.title).string : presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
return true
}))
case let .remove(positionInList):
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedText(info.title).string : presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { action in
if case .undo = action {
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
}
return true
}))
}
}
})
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
} }
@ -9412,7 +9441,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} else { } else {
canSendMedia = true canSendMedia = true
} }
if canSendMedia { if canSendMedia && self.presentationInterfaceState.voiceMessagesAvailable {
let _ = (ApplicationSpecificNotice.getChatMediaMediaRecordingTips(accountManager: self.context.sharedContext.accountManager) let _ = (ApplicationSpecificNotice.getChatMediaMediaRecordingTips(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { [weak self] counter in |> deliverOnMainQueue).start(next: { [weak self] counter in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -16304,6 +16333,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
} }
public func hintPlayNextOutgoingGift() {
self.controllerInteraction?.playNextOutgoingGift = true
}
private var effectiveNavigationController: NavigationController? { private var effectiveNavigationController: NavigationController? {
if let navigationController = self.navigationController as? NavigationController { if let navigationController = self.navigationController as? NavigationController {
return navigationController return navigationController

View File

@ -160,6 +160,7 @@ public final class ChatControllerInteraction {
var currentMessageWithLoadingReplyThread: MessageId? var currentMessageWithLoadingReplyThread: MessageId?
var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
let presentationContext: ChatPresentationContext let presentationContext: ChatPresentationContext
var playNextOutgoingGift: Bool = false
init( init(
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,

View File

@ -214,6 +214,9 @@ class ChatMessageBubbleContentNode: ASDisplayNode {
func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
} }
func unreadMessageRangeUpdated() {
}
func reactionTargetView(value: String) -> UIView? { func reactionTargetView(value: String) -> UIView? {
return nil return nil
} }

View File

@ -3975,6 +3975,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
return nil return nil
} }
override func unreadMessageRangeUpdated() {
for contentNode in self.contentNodes {
contentNode.unreadMessageRangeUpdated()
}
}
func animateQuizInvalidOptionSelected() { func animateQuizInvalidOptionSelected() {
if let supernode = self.supernode, let subnodes = supernode.subnodes { if let supernode = self.supernode, let subnodes = supernode.subnodes {
for i in 0 ..< subnodes.count { for i in 0 ..< subnodes.count {

View File

@ -41,8 +41,6 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private var absoluteRect: (CGRect, CGSize)? private var absoluteRect: (CGRect, CGSize)?
private var isPlaying: Bool = false private var isPlaying: Bool = false
private var wasPending: Bool = false
private var didChangeFromPendingToSent: Bool = false
override var visibility: ListViewItemNodeVisibility { override var visibility: ListViewItemNodeVisibility {
didSet { didSet {
@ -165,9 +163,9 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string
switch months { switch months {
case 12: case 12:
animationName = "Gift2" animationName = "Gift12"
case 6: case 6:
animationName = "Gift1" animationName = "Gift6"
case 3: case 3:
animationName = "Gift3" animationName = "Gift3"
default: default:
@ -228,12 +226,6 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
strongSelf.item = item strongSelf.item = item
if item.message.id.namespace == Namespaces.Message.Local || item.message.id.namespace == Namespaces.Message.ScheduledLocal {
strongSelf.wasPending = true
}
if strongSelf.wasPending && (item.message.id.namespace != Namespaces.Message.Local && item.message.id.namespace != Namespaces.Message.ScheduledLocal) {
strongSelf.didChangeFromPendingToSent = true
}
strongSelf.updateVisibility() strongSelf.updateVisibility()
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
@ -416,6 +408,10 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
override func unreadMessageRangeUpdated() {
self.updateVisibility()
}
private func updateVisibility() { private func updateVisibility() {
guard let item = self.item else { guard let item = self.item else {
return return
@ -430,12 +426,16 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
if isPlaying { if isPlaying {
var alreadySeen = true var alreadySeen = true
if item.message.flags.contains(.Incoming) {
if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] { if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] {
if unreadRange.contains(item.message.id.id) { if unreadRange.contains(item.message.id.id) {
if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
alreadySeen = false alreadySeen = false
} }
} }
} else {
if item.controllerInteraction.playNextOutgoingGift && !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
alreadySeen = false
}
} }
if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) { if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
@ -444,8 +444,10 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
if !alreadySeen { if !alreadySeen {
Queue.mainQueue().after(1.0) {
item.controllerInteraction.animateDiceSuccess(false, true) item.controllerInteraction.animateDiceSuccess(false, true)
} }
} }
} }
} }
}

View File

@ -363,7 +363,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
let colorKeys = ["__allcolors__"] let colorKeys = ["__allcolors__"]
var colors: [String: UIColor] = [:] var colors: [String: UIColor] = [:]
for colorKey in colorKeys { for colorKey in colorKeys {
colors[colorKey] = self.theme.chat.inputPanel.panelControlColor colors[colorKey] = self.theme.chat.inputPanel.panelControlColor.blitOver(self.theme.chat.inputPanel.inputBackgroundColor, alpha: 1.0)
} }
let _ = animationView.update( let _ = animationView.update(
@ -497,8 +497,11 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
micDecoration.isHidden = false micDecoration.isHidden = false
micDecoration.startAnimating() micDecoration.startAnimating()
self.animationView.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) let transition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
self.animationView.view?.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false) if let layer = self.animationView.view?.layer {
transition.updateAlpha(layer: layer, alpha: 0.0)
transition.updateTransformScale(layer: layer, scale: 0.3)
}
} }
override func animateOut(_ toSmallSize: Bool) { override func animateOut(_ toSmallSize: Bool) {
@ -510,8 +513,11 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.03, delay: 0.15, removeOnCompletion: false) micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.03, delay: 0.15, removeOnCompletion: false)
} else { } else {
micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false) micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false)
self.animationView.view?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false) let transition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
self.animationView.view?.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, removeOnCompletion: false) if let layer = self.animationView.view?.layer {
transition.updateAlpha(layer: layer, alpha: 1.0)
transition.updateTransformScale(layer: layer, scale: 1.0)
}
} }
} }

View File

@ -78,10 +78,18 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
params.navigationController?.pushViewController(controller) params.navigationController?.pushViewController(controller)
return true return true
case let .stickerPack(reference): case let .stickerPack(reference):
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { info, items, action in let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { actions in
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
if actions.count > 1, let first = actions.first {
if case .add = first.2 {
params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.EmojiPackActionInfo_AddedTitle, text: presentationData.strings.EmojiPackActionInfo_MultipleAddedText(Int32(actions.count)), undo: false, info: first.0, topItem: first.1.first, context: params.context), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
return true
}))
}
} else if let (info, items, action) = actions.first {
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
var animateInAsReplacement = false var animateInAsReplacement = false
if let navigationController = params.navigationController { if let navigationController = params.navigationController {
for controller in navigationController.overlayControllers { for controller in navigationController.overlayControllers {
@ -104,6 +112,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
return true return true
})) }))
} }
}
}) })
params.dismissInput() params.dismissInput()
params.present(controller, nil) params.present(controller, nil)

View File

@ -166,7 +166,14 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
case let .stickerPack(name, _): case let .stickerPack(name, _):
dismissInput() dismissInput()
let controller = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: .name(name), stickerPacks: [.name(name)], parentNavigationController: navigationController, sendSticker: sendSticker, actionPerformed: { info, items, action in let controller = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: .name(name), stickerPacks: [.name(name)], parentNavigationController: navigationController, sendSticker: sendSticker, actionPerformed: { actions in
if actions.count > 1, let first = actions.first {
if case .add = first.2 {
present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.EmojiPackActionInfo_AddedTitle, text: presentationData.strings.EmojiPackActionInfo_MultipleAddedText(Int32(actions.count)), undo: false, info: first.0, topItem: first.1.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
return true
}), nil)
}
} else if let (info, items, action) = actions.first {
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
switch action { switch action {
@ -182,6 +189,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
return true return true
}), nil) }), nil)
} }
}
}) })
present(controller, nil) present(controller, nil)
case let .instantView(webpage, anchor): case let .instantView(webpage, anchor):

View File

@ -1,5 +1,5 @@
{ {
"app": "8.8.3", "app": "8.9",
"bazel": "5.1.0", "bazel": "5.1.0",
"xcode": "13.4.1" "xcode": "13.4.1"
} }