mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge commit 'f1753011e1e5ca79e4720288fd7a521344ef6373'
This commit is contained in:
commit
235482fea1
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Gift12.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Gift12.tgs
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Gift6.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Gift6.tgs
Normal file
Binary file not shown.
@ -7869,6 +7869,7 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Premium.AnimatedEmoji" = "Animated Emoji";
|
||||
"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.EmojiSet_1" = "This message contains emoji from [%@ pack]().";
|
||||
@ -7886,6 +7887,12 @@ Sorry for the inconvenience.";
|
||||
"EmojiPackActionInfo.ArchivedTitle" = "Emoji Archived";
|
||||
"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.ArchivedTitle" = "Masks Archived";
|
||||
"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.";
|
||||
|
||||
"StickerPack.CopyLinks" = "Copy Links";
|
||||
"Conversation.LinksCopied" = "Links copied to clipboard";
|
||||
|
||||
"StickersList.EmojiItem" = "Custom Emoji";
|
||||
"StickersList.ArchivedEmojiItem" = "Archived Emoji";
|
||||
|
||||
|
@ -560,6 +560,8 @@ public protocol ChatController: ViewController {
|
||||
func beginMessageSearch(_ query: String)
|
||||
func displayPromoAnnouncement(text: String)
|
||||
|
||||
func hintPlayNextOutgoingGift()
|
||||
|
||||
var isSendButtonVisible: Bool { get }
|
||||
}
|
||||
|
||||
|
@ -481,11 +481,13 @@ private func availablePaymentMethods(form: BotPaymentForm, current: BotCheckoutP
|
||||
methods.append(.applePay)
|
||||
hasApplePay = true
|
||||
}
|
||||
if let savedCredentials = form.savedCredentials {
|
||||
|
||||
for savedCredentials in form.savedCredentials {
|
||||
if !methods.contains(.savedCredentials(savedCredentials)) {
|
||||
methods.append(.savedCredentials(savedCredentials))
|
||||
}
|
||||
}
|
||||
|
||||
if !form.additionalPaymentMethods.isEmpty {
|
||||
methods.append(contentsOf: form.additionalPaymentMethods.map { .other($0) })
|
||||
}
|
||||
@ -835,7 +837,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
}
|
||||
|
||||
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
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1106,7 +1108,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
strongSelf.botPeerValue = formAndValidatedInfo.botPeer
|
||||
strongSelf.currentFormInfo = savedInfo
|
||||
strongSelf.currentValidatedFormInfo = formAndValidatedInfo.validatedFormInfo
|
||||
if let savedCredentials = formAndValidatedInfo.form.savedCredentials {
|
||||
if let savedCredentials = formAndValidatedInfo.form.savedCredentials.first {
|
||||
strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials)
|
||||
}
|
||||
strongSelf.actionButton.isEnabled = true
|
||||
|
@ -471,7 +471,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
if self.animateInputField {
|
||||
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.textInputNode.isHidden = true
|
||||
} else {
|
||||
self.messageBackgroundNode.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)
|
||||
|
||||
Queue.mainQueue().after(0.01, {
|
||||
if self.animateInputField {
|
||||
self.textInputNode.isHidden = true
|
||||
}
|
||||
self.updateTextContents()
|
||||
})
|
||||
}
|
||||
|
@ -662,14 +662,17 @@ public class Window1 {
|
||||
return
|
||||
}
|
||||
self.forceBadgeHidden = hidden
|
||||
self.updateBadgeVisibility(layout: self.windowLayout)
|
||||
self.updateBadgeVisibility()
|
||||
}
|
||||
|
||||
private func updateBadgeVisibility(layout: WindowLayout) {
|
||||
let badgeIsHidden = !self.deviceMetrics.hasTopNotch || self.forceBadgeHidden || layout.size.width > layout.size.height
|
||||
private func updateBadgeVisibility() {
|
||||
let badgeIsHidden = !self.deviceMetrics.hasTopNotch || self.forceBadgeHidden || self.windowLayout.size.width > self.windowLayout.size.height
|
||||
if badgeIsHidden != self.badgeView.isHidden && !badgeIsHidden {
|
||||
Queue.mainQueue().after(0.3) {
|
||||
self.badgeView.isHidden = badgeIsHidden
|
||||
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
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.badgeView.isHidden = badgeIsHidden
|
||||
@ -1115,7 +1118,7 @@ public class Window1 {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -474,20 +474,22 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
let baseNavigationController = strongSelf.baseNavigationController()
|
||||
baseNavigationController?.view.endEditing(true)
|
||||
let controller = StickerPackScreen(context: context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in
|
||||
let animateInAsReplacement = false
|
||||
switch action {
|
||||
case .add:
|
||||
topController?.present(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: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
case let .remove(positionInList):
|
||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
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
|
||||
switch action {
|
||||
case .add:
|
||||
topController?.present(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: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
case let .remove(positionInList):
|
||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
}
|
||||
}
|
||||
})
|
||||
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
|
||||
|
@ -2476,16 +2476,16 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
})))
|
||||
|
||||
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
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.beginAirPlaySetup()
|
||||
})))
|
||||
}
|
||||
|
||||
// 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
|
||||
// f(.default)
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.beginAirPlaySetup()
|
||||
// })))
|
||||
// }
|
||||
|
||||
if let (message, _, _) = strongSelf.contentInfo() {
|
||||
for media in message.media {
|
||||
@ -2667,20 +2667,22 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
let baseNavigationController = strongSelf.baseNavigationController()
|
||||
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 animateInAsReplacement = false
|
||||
switch action {
|
||||
case .add:
|
||||
topController?.present(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: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
case let .remove(positionInList):
|
||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
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
|
||||
switch action {
|
||||
case .add:
|
||||
topController?.present(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: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
case let .remove(positionInList):
|
||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}, dismissed: { [weak self] in
|
||||
self?.isInteractingPromise.set(false)
|
||||
|
@ -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
|
||||
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))
|
||||
}
|
||||
return true
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 6.0 KiB |
@ -909,7 +909,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
decoration: .emoji
|
||||
)),
|
||||
title: strings.Premium_AnimatedEmoji,
|
||||
text: strings.Premium_AnimatedEmojiInfo,
|
||||
text: isStandalone ? strings.Premium_AnimatedEmojiStandaloneInfo : strings.Premium_AnimatedEmojiInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
|
@ -145,7 +145,7 @@ private final class ProductGroupComponent: Component {
|
||||
buttonView?.backgroundColor = component.selectionColor
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
buttonView?.backgroundColor = nil
|
||||
buttonView?.backgroundColor = component.backgroundColor
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -299,31 +299,44 @@ private final class GiftComponent: CombinedComponent {
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let discount = discount.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(
|
||||
NSAttributedString(
|
||||
string: component.discount,
|
||||
font: Font.with(size: 14.0, design: .round, weight: .semibold, traits: []),
|
||||
textColor: .white
|
||||
)
|
||||
let discountSize: CGSize
|
||||
if !component.discount.isEmpty {
|
||||
let discount = discount.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(
|
||||
NSAttributedString(
|
||||
string: component.discount,
|
||||
font: Font.with(size: 14.0, design: .round, weight: .semibold, traits: []),
|
||||
textColor: .white
|
||||
)
|
||||
),
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
discountSize = CGSize(width: discount.size.width + 6.0, height: 18.0)
|
||||
|
||||
let discountSize = CGSize(width: discount.size.width + 6.0, height: 18.0)
|
||||
|
||||
let discountBackground = discountBackground.update(
|
||||
component: RoundedRectangle(
|
||||
color: component.accentColor,
|
||||
cornerRadius: 5.0
|
||||
),
|
||||
availableSize: discountSize,
|
||||
transition: context.transition
|
||||
)
|
||||
let discountBackground = discountBackground.update(
|
||||
component: RoundedRectangle(
|
||||
color: component.accentColor,
|
||||
cornerRadius: 5.0
|
||||
),
|
||||
availableSize: discountSize,
|
||||
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(
|
||||
component: MultilineTextComponent(
|
||||
@ -359,17 +372,9 @@ private final class GiftComponent: CombinedComponent {
|
||||
context.add(title
|
||||
.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
|
||||
.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)
|
||||
@ -510,13 +515,13 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer?
|
||||
let products: [InAppPurchaseManager.Product]?
|
||||
let products: [PremiumGiftProduct]?
|
||||
let selectedProductId: String?
|
||||
|
||||
let present: (ViewController) -> 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.peer = peer
|
||||
self.products = products
|
||||
@ -629,27 +634,27 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
|
||||
var i = 0
|
||||
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 {
|
||||
let monthsCount: Int
|
||||
let giftTitle: String
|
||||
if product.months == 12 {
|
||||
giftTitle = strings.Premium_Gift_Years(1)
|
||||
} else {
|
||||
giftTitle = strings.Premium_Gift_Months(product.months)
|
||||
}
|
||||
|
||||
let discountValue = Int((1.0 - Float(product.storeProduct.priceCurrencyAndAmount.amount) / Float(product.months) / Float(shortestOptionPrice)) * 100.0)
|
||||
let discount: String
|
||||
switch product.id {
|
||||
case "org.telegram.telegramPremium.twelveMonths":
|
||||
giftTitle = strings.Premium_Gift_Years(1)
|
||||
monthsCount = 12
|
||||
discount = "-15%"
|
||||
case "org.telegram.telegramPremium.sixMonths":
|
||||
giftTitle = strings.Premium_Gift_Months(6)
|
||||
monthsCount = 6
|
||||
discount = "-10%"
|
||||
case "org.telegram.telegramPremium.threeMonths":
|
||||
giftTitle = strings.Premium_Gift_Months(3)
|
||||
monthsCount = 3
|
||||
discount = "-7%"
|
||||
default:
|
||||
giftTitle = ""
|
||||
monthsCount = 1
|
||||
discount = ""
|
||||
if discountValue > 0 {
|
||||
discount = "-\(discountValue)%"
|
||||
} else {
|
||||
discount = ""
|
||||
}
|
||||
|
||||
items.append(ProductGroupComponent.Item(
|
||||
@ -659,7 +664,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
GiftComponent(
|
||||
title: giftTitle,
|
||||
totalPrice: product.price,
|
||||
perMonthPrice: strings.Premium_Gift_PricePerMonth(product.pricePerMonth(monthsCount)).string,
|
||||
perMonthPrice: strings.Premium_Gift_PricePerMonth(product.pricePerMonth).string,
|
||||
discount: discount,
|
||||
selected: product.id == component.selectedProductId,
|
||||
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 {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let peerId: PeerId
|
||||
let options: [CachedPremiumGiftOption]
|
||||
let updateInProgress: (Bool) -> Void
|
||||
let present: (ViewController) -> Void
|
||||
let push: (ViewController) -> 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.peerId = peerId
|
||||
self.options = options
|
||||
self.updateInProgress = updateInProgress
|
||||
self.present = present
|
||||
self.push = push
|
||||
@ -731,12 +759,16 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
if lhs.peerId != rhs.peerId {
|
||||
return false
|
||||
}
|
||||
if lhs.options != rhs.options {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let options: [CachedPremiumGiftOption]
|
||||
private let updateInProgress: (Bool) -> Void
|
||||
private let present: (ViewController) -> Void
|
||||
private let completion: (Int32) -> Void
|
||||
@ -749,16 +781,17 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
var inProgress = false
|
||||
|
||||
var peer: EnginePeer?
|
||||
var products: [InAppPurchaseManager.Product]?
|
||||
var products: [PremiumGiftProduct]?
|
||||
var selectedProductId: String?
|
||||
|
||||
private var disposable: Disposable?
|
||||
private var paymentDisposable = 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.peerId = peerId
|
||||
self.options = options
|
||||
self.updateInProgress = updateInProgress
|
||||
self.present = present
|
||||
self.completion = completion
|
||||
@ -778,8 +811,18 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
).start(next: { [weak self] products, peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.products = products.filter { !$0.isSubscription }.sorted(by: { $0.priceValue.compare($1.priceValue) == .orderedDescending })
|
||||
strongSelf.selectedProductId = strongSelf.products?.first?.id
|
||||
|
||||
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.peer = peer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
let (currency, amount) = product.priceCurrencyAndAmount
|
||||
|
||||
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
|
||||
}
|
||||
let (currency, amount) = product.storeProduct.priceCurrencyAndAmount
|
||||
let duration = product.months
|
||||
|
||||
// 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
|
||||
if let strongSelf = self {
|
||||
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
|
||||
if let strongSelf = self, case .purchased = status {
|
||||
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)
|
||||
|
||||
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
|
||||
@ -896,7 +928,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
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: {})])
|
||||
strongSelf.present(alertController)
|
||||
@ -919,7 +951,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -1242,6 +1274,7 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
||||
super.init(context: context, component: PremiumGiftScreenComponent(
|
||||
context: context,
|
||||
peerId: peerId,
|
||||
options: options,
|
||||
updateInProgress: { inProgress in
|
||||
updateInProgressImpl?(inProgress)
|
||||
},
|
||||
@ -1278,6 +1311,12 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
case let .privacyHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
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()
|
||||
})
|
||||
case let .phoneNumberPrivacy(_, text, value):
|
||||
|
@ -1267,7 +1267,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
||||
packs.insert(packReference, at: 0)
|
||||
}
|
||||
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 }
|
||||
var animateInAsReplacement = false
|
||||
if let navigationController = navigationControllerImpl?() {
|
||||
@ -1278,31 +1278,33 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
||||
}
|
||||
}
|
||||
}
|
||||
switch action {
|
||||
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
|
||||
return true
|
||||
}))
|
||||
case let .remove(positionInList):
|
||||
let removedTitle: String
|
||||
let removedText: String
|
||||
if info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks {
|
||||
removedTitle = presentationData.strings.EmojiPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.EmojiPackActionInfo_RemovedText(info.title).string
|
||||
} else if info.id.namespace == Namespaces.ItemCollection.CloudMaskPacks {
|
||||
removedTitle = presentationData.strings.MaskPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.MaskPackActionInfo_RemovedText(info.title).string
|
||||
} else {
|
||||
removedTitle = presentationData.strings.StickerPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string
|
||||
}
|
||||
|
||||
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: removedTitle, text: removedText, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
if let (info, items, action) = actions.first {
|
||||
switch action {
|
||||
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
|
||||
return true
|
||||
}))
|
||||
case let .remove(positionInList):
|
||||
let removedTitle: String
|
||||
let removedText: String
|
||||
if info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks {
|
||||
removedTitle = presentationData.strings.EmojiPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.EmojiPackActionInfo_RemovedText(info.title).string
|
||||
} else if info.id.namespace == Namespaces.ItemCollection.CloudMaskPacks {
|
||||
removedTitle = presentationData.strings.MaskPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.MaskPackActionInfo_RemovedText(info.title).string
|
||||
} else {
|
||||
removedTitle = presentationData.strings.StickerPackActionInfo_RemovedTitle
|
||||
removedText = presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string
|
||||
}
|
||||
return true
|
||||
}))
|
||||
|
||||
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: removedTitle, text: removedText, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}))
|
||||
}
|
||||
}
|
||||
}), nil)
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
lockBackground.isUserInteractionEnabled = false
|
||||
lockIconNode = ASImageNode()
|
||||
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()
|
||||
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 {
|
||||
let lockSize = CGSize(width: 30.0, height: 30.0)
|
||||
let lockBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - lockSize.width) / 2.0), y: bounds.height - lockSize.height - 6.0), size: lockSize)
|
||||
let lockSize = CGSize(width: 16.0, height: 16.0)
|
||||
let lockBackgroundFrame = CGRect(origin: CGPoint(x: bounds.width - lockSize.width, y: bounds.height - lockSize.height), size: lockSize)
|
||||
lockBackground.frame = lockBackgroundFrame
|
||||
lockBackground.layer.cornerRadius = lockSize.width / 2.0
|
||||
if #available(iOS 13.0, *) {
|
||||
@ -330,7 +330,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
}
|
||||
lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -586,7 +586,10 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
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)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
@ -595,7 +598,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|
||||
if let strongSelf = self {
|
||||
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 {
|
||||
var installedPacks: [(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)] = []
|
||||
for (info, items, isInstalled) in self.currentStickerPacks {
|
||||
if !isInstalled {
|
||||
installedPacks.append((info, items, .add))
|
||||
let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
|
||||
// if dismissed {
|
||||
// actionPerformed?(info, items, .add)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
self.controller?.actionPerformed?(installedPacks)
|
||||
}
|
||||
self.requestDismiss()
|
||||
} else if let (info, items, installed) = self.currentStickerPack {
|
||||
@ -657,13 +661,13 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if dismissed {
|
||||
actionPerformed?(info, items, .remove(positionInList: positionInList))
|
||||
actionPerformed?([(info, items, .remove(positionInList: positionInList))])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
|
||||
if dismissed {
|
||||
actionPerformed?(info, items, .add)
|
||||
actionPerformed?([(info, items, .add)])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1217,7 +1221,6 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
super.didLoad()
|
||||
|
||||
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) {
|
||||
@ -1235,11 +1238,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
transition.updateFrame(node: self.containerContainingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
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 scaledDistance: CGFloat = 4.0
|
||||
let minScale = (layout.size.width - scaledInset * 2.0) / layout.size.width
|
||||
@ -1247,108 +1246,106 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
|
||||
let containerVerticalOffset: CGFloat = (1.0 - expandProgress) * scaledInset * 2.0
|
||||
|
||||
// for i in 0 ..< self.stickerPacks.count {
|
||||
let i = 0
|
||||
let indexOffset = i - self.selectedStickerPackIndex
|
||||
var scaledOffset: CGFloat = 0.0
|
||||
scaledOffset = -CGFloat(indexOffset) * (1.0 - expandProgress) * (scaledInset * 2.0) + CGFloat(indexOffset) * scaledDistance
|
||||
|
||||
if abs(indexOffset) <= 1 {
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
let container: StickerPackContainer
|
||||
var wasAdded = false
|
||||
if let current = self.containers[i] {
|
||||
containerTransition = transition
|
||||
container = current
|
||||
} else {
|
||||
wasAdded = true
|
||||
containerTransition = .immediate
|
||||
let index = i
|
||||
container = StickerPackContainer(index: index, context: context, presentationData: self.presentationData, stickerPacks: self.stickerPacks, decideNextAction: { [weak self] container, action in
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout else {
|
||||
return .dismiss
|
||||
}
|
||||
if index == strongSelf.stickerPacks.count - 1 {
|
||||
return .dismiss
|
||||
} else {
|
||||
switch action {
|
||||
case .add:
|
||||
var allAdded = true
|
||||
for _ in index + 1 ..< strongSelf.stickerPacks.count {
|
||||
if let container = strongSelf.containers[index], let (_, _, installed) = container.currentStickerPack {
|
||||
if !installed {
|
||||
allAdded = false
|
||||
}
|
||||
} else {
|
||||
let indexOffset = i - self.selectedStickerPackIndex
|
||||
var scaledOffset: CGFloat = 0.0
|
||||
scaledOffset = -CGFloat(indexOffset) * (1.0 - expandProgress) * (scaledInset * 2.0) + CGFloat(indexOffset) * scaledDistance
|
||||
|
||||
if abs(indexOffset) <= 1 {
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
let container: StickerPackContainer
|
||||
var wasAdded = false
|
||||
if let current = self.containers[i] {
|
||||
containerTransition = transition
|
||||
container = current
|
||||
} else {
|
||||
wasAdded = true
|
||||
containerTransition = .immediate
|
||||
let index = i
|
||||
container = StickerPackContainer(index: index, context: context, presentationData: self.presentationData, stickerPacks: self.stickerPacks, decideNextAction: { [weak self] container, action in
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout else {
|
||||
return .dismiss
|
||||
}
|
||||
if index == strongSelf.stickerPacks.count - 1 {
|
||||
return .dismiss
|
||||
} else {
|
||||
switch action {
|
||||
case .add:
|
||||
var allAdded = true
|
||||
for _ in index + 1 ..< strongSelf.stickerPacks.count {
|
||||
if let container = strongSelf.containers[index], let (_, _, installed) = container.currentStickerPack {
|
||||
if !installed {
|
||||
allAdded = false
|
||||
}
|
||||
}
|
||||
if allAdded {
|
||||
return .dismiss
|
||||
}
|
||||
case .remove:
|
||||
if strongSelf.stickerPacks.count == 1 {
|
||||
return .dismiss
|
||||
} else {
|
||||
return .ignored
|
||||
allAdded = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.selectedStickerPackIndex = strongSelf.selectedStickerPackIndex + 1
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring))
|
||||
return .navigatedNext
|
||||
}, requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
}, expandProgressUpdated: { [weak self] container, transition, expandTransition in
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout else {
|
||||
return
|
||||
}
|
||||
if index == strongSelf.selectedStickerPackIndex, let container = strongSelf.containers[strongSelf.selectedStickerPackIndex] {
|
||||
let modalProgress = container.modalProgress
|
||||
strongSelf.modalProgressUpdated(modalProgress, transition)
|
||||
strongSelf.containerLayoutUpdated(layout, transition: expandTransition)
|
||||
for (_, otherContainer) in strongSelf.containers {
|
||||
if otherContainer !== container {
|
||||
otherContainer.syncExpandProgress(expandScrollProgress: container.expandScrollProgress, expandProgress: container.expandProgress, modalProgress: container.modalProgress, transition: expandTransition)
|
||||
}
|
||||
if allAdded {
|
||||
return .dismiss
|
||||
}
|
||||
case .remove:
|
||||
if strongSelf.stickerPacks.count == 1 {
|
||||
return .dismiss
|
||||
} else {
|
||||
return .ignored
|
||||
}
|
||||
}
|
||||
}, presentInGlobalOverlay: presentInGlobalOverlay, sendSticker: sendSticker, openMention: openMention, controller: self.controller)
|
||||
container.onReady = { [weak self] in
|
||||
self?.onReady()
|
||||
}
|
||||
container.onLoading = { [weak self] in
|
||||
self?.onLoading()
|
||||
|
||||
strongSelf.selectedStickerPackIndex = strongSelf.selectedStickerPackIndex + 1
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring))
|
||||
return .navigatedNext
|
||||
}, requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
}, expandProgressUpdated: { [weak self] container, transition, expandTransition in
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout else {
|
||||
return
|
||||
}
|
||||
container.onError = { [weak self] in
|
||||
self?.onError()
|
||||
}
|
||||
self.containerContainingNode.addSubnode(container)
|
||||
self.containers[i] = container
|
||||
}
|
||||
|
||||
let containerFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * layout.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: layout.size)
|
||||
containerTransition.updateFrame(node: container, frame: containerFrame, beginWithCurrentState: true)
|
||||
containerTransition.updateSublayerTransformScaleAndOffset(node: container, scale: containerScale, offset: CGPoint(), beginWithCurrentState: true)
|
||||
if container.validLayout?.0 != layout {
|
||||
container.updateLayout(layout: layout, transition: containerTransition)
|
||||
}
|
||||
|
||||
if wasAdded {
|
||||
if let selectedContainer = self.containers[self.selectedStickerPackIndex] {
|
||||
if selectedContainer !== container {
|
||||
container.syncExpandProgress(expandScrollProgress: selectedContainer.expandScrollProgress, expandProgress: selectedContainer.expandProgress, modalProgress: selectedContainer.modalProgress, transition: .immediate)
|
||||
if index == strongSelf.selectedStickerPackIndex, let container = strongSelf.containers[strongSelf.selectedStickerPackIndex] {
|
||||
let modalProgress = container.modalProgress
|
||||
strongSelf.modalProgressUpdated(modalProgress, transition)
|
||||
strongSelf.containerLayoutUpdated(layout, transition: expandTransition)
|
||||
for (_, otherContainer) in strongSelf.containers {
|
||||
if otherContainer !== container {
|
||||
otherContainer.syncExpandProgress(expandScrollProgress: container.expandScrollProgress, expandProgress: container.expandProgress, modalProgress: container.modalProgress, transition: expandTransition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, presentInGlobalOverlay: presentInGlobalOverlay, sendSticker: sendSticker, openMention: openMention, controller: self.controller)
|
||||
container.onReady = { [weak self] in
|
||||
self?.onReady()
|
||||
}
|
||||
} else {
|
||||
if let container = self.containers[i] {
|
||||
container.removeFromSupernode()
|
||||
self.containers.removeValue(forKey: i)
|
||||
container.onLoading = { [weak self] in
|
||||
self?.onLoading()
|
||||
}
|
||||
container.onError = { [weak self] in
|
||||
self?.onError()
|
||||
}
|
||||
self.containerContainingNode.addSubnode(container)
|
||||
self.containers[i] = container
|
||||
}
|
||||
|
||||
let containerFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * layout.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: layout.size)
|
||||
containerTransition.updateFrame(node: container, frame: containerFrame, beginWithCurrentState: true)
|
||||
containerTransition.updateSublayerTransformScaleAndOffset(node: container, scale: containerScale, offset: CGPoint(), beginWithCurrentState: true)
|
||||
if container.validLayout?.0 != layout {
|
||||
container.updateLayout(layout: layout, transition: containerTransition)
|
||||
}
|
||||
|
||||
if wasAdded {
|
||||
if let selectedContainer = self.containers[self.selectedStickerPackIndex] {
|
||||
if selectedContainer !== container {
|
||||
container.syncExpandProgress(expandScrollProgress: selectedContainer.expandScrollProgress, expandProgress: selectedContainer.expandProgress, modalProgress: selectedContainer.modalProgress, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
if let container = self.containers[i] {
|
||||
container.removeFromSupernode()
|
||||
self.containers.removeValue(forKey: i)
|
||||
}
|
||||
}
|
||||
|
||||
if firstTime {
|
||||
if !self.containers.isEmpty {
|
||||
@ -1512,7 +1509,7 @@ public final class StickerPackScreenImpl: ViewController {
|
||||
}
|
||||
|
||||
public var dismissed: (() -> Void)?
|
||||
public var actionPerformed: ((StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction) -> Void)?
|
||||
public var actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)?
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
@ -1527,7 +1524,7 @@ public final class StickerPackScreenImpl: ViewController {
|
||||
let animationCache: AnimationCache
|
||||
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.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.stickerPacks = stickerPacks
|
||||
@ -1706,7 +1703,7 @@ public enum StickerPackScreenPerformedAction {
|
||||
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 controller = StickerPackScreenImpl(context: context, stickerPacks: stickerPacks, selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: actionPerformed)
|
||||
controller.dismissed = dismissed
|
||||
|
@ -1006,7 +1006,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($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[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
||||
|
@ -728,13 +728,13 @@ public extension Api.payments {
|
||||
}
|
||||
public extension Api.payments {
|
||||
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) {
|
||||
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):
|
||||
if boxed {
|
||||
buffer.appendInt32(1288001087)
|
||||
buffer.appendInt32(-1610250415)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(formId, buffer: buffer, boxed: false)
|
||||
@ -753,7 +753,11 @@ public extension Api.payments {
|
||||
item.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(Int32(users.count))
|
||||
for item in users {
|
||||
@ -807,9 +811,9 @@ public extension Api.payments {
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
|
||||
} }
|
||||
var _14: Api.PaymentSavedCredentials?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_14 = Api.parse(reader, signature: signature) as? Api.PaymentSavedCredentials
|
||||
var _14: [Api.PaymentSavedCredentials]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self)
|
||||
} }
|
||||
var _15: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
|
@ -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] {
|
||||
self.contexts.removeValue(forKey: messageId)
|
||||
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
|
||||
impl.add(messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview)
|
||||
}
|
||||
|
@ -27,11 +27,11 @@ public enum RequestEditMessageError {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|> `catch` { error -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> in
|
||||
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>
|
||||
switch media {
|
||||
case .keep:
|
||||
|
@ -85,7 +85,7 @@ public extension TelegramEngine {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ public struct BotPaymentForm : Equatable {
|
||||
public let url: String
|
||||
public let nativeProvider: BotPaymentNativeProvider?
|
||||
public let savedInfo: BotPaymentRequestedInfo?
|
||||
public let savedCredentials: BotPaymentSavedCredentials?
|
||||
public let savedCredentials: [BotPaymentSavedCredentials]
|
||||
public let additionalPaymentMethods: [BotPaymentMethod]
|
||||
}
|
||||
|
||||
@ -304,14 +304,13 @@ func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: B
|
||||
}
|
||||
}
|
||||
let parsedSavedInfo = savedInfo.flatMap(BotPaymentRequestedInfo.init)
|
||||
var parsedSavedCredentials: BotPaymentSavedCredentials?
|
||||
if let savedCredentials = savedCredentials {
|
||||
let parsedSavedCredentials = savedCredentials?.map({ savedCredentials -> BotPaymentSavedCredentials in
|
||||
switch savedCredentials {
|
||||
case let .paymentSavedCredentialsCard(id, title):
|
||||
parsedSavedCredentials = .card(id: id, title: title)
|
||||
return .card(id: id, title: title)
|
||||
}
|
||||
}
|
||||
|
||||
}) ?? []
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -1093,7 +1093,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
var disableTransitionAnimations = false
|
||||
var actionsSignal: Signal<ContextController.Items, NoError> = .single(actions)
|
||||
if actions.tip == nil, let entitiesAttribute = message.textEntitiesAttribute {
|
||||
if let entitiesAttribute = message.textEntitiesAttribute {
|
||||
var emojiFileIds: [Int64] = []
|
||||
for entity in entitiesAttribute.entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
@ -1128,7 +1128,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@ -9412,7 +9441,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else {
|
||||
canSendMedia = true
|
||||
}
|
||||
if canSendMedia {
|
||||
if canSendMedia && self.presentationInterfaceState.voiceMessagesAvailable {
|
||||
let _ = (ApplicationSpecificNotice.getChatMediaMediaRecordingTips(accountManager: self.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||
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? {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
return navigationController
|
||||
|
@ -160,6 +160,7 @@ public final class ChatControllerInteraction {
|
||||
var currentMessageWithLoadingReplyThread: MessageId?
|
||||
var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
let presentationContext: ChatPresentationContext
|
||||
var playNextOutgoingGift: Bool = false
|
||||
|
||||
init(
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
|
@ -214,6 +214,9 @@ class ChatMessageBubbleContentNode: ASDisplayNode {
|
||||
func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
}
|
||||
|
||||
func unreadMessageRangeUpdated() {
|
||||
}
|
||||
|
||||
func reactionTargetView(value: String) -> UIView? {
|
||||
return nil
|
||||
}
|
||||
|
@ -3975,6 +3975,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
return nil
|
||||
}
|
||||
|
||||
override func unreadMessageRangeUpdated() {
|
||||
for contentNode in self.contentNodes {
|
||||
contentNode.unreadMessageRangeUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
func animateQuizInvalidOptionSelected() {
|
||||
if let supernode = self.supernode, let subnodes = supernode.subnodes {
|
||||
for i in 0 ..< subnodes.count {
|
||||
|
@ -41,8 +41,6 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
|
||||
private var isPlaying: Bool = false
|
||||
private var wasPending: Bool = false
|
||||
private var didChangeFromPendingToSent: Bool = false
|
||||
|
||||
override var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
@ -165,9 +163,9 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string
|
||||
switch months {
|
||||
case 12:
|
||||
animationName = "Gift2"
|
||||
animationName = "Gift12"
|
||||
case 6:
|
||||
animationName = "Gift1"
|
||||
animationName = "Gift6"
|
||||
case 3:
|
||||
animationName = "Gift3"
|
||||
default:
|
||||
@ -227,13 +225,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: 384, height: 384, playbackMode: .still(.end), mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
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.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() {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
@ -430,12 +426,16 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if isPlaying {
|
||||
var alreadySeen = true
|
||||
|
||||
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 !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
|
||||
if item.message.flags.contains(.Incoming) {
|
||||
if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] {
|
||||
if unreadRange.contains(item.message.id.id) {
|
||||
alreadySeen = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if item.controllerInteraction.playNextOutgoingGift && !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
|
||||
alreadySeen = false
|
||||
}
|
||||
}
|
||||
|
||||
if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) {
|
||||
@ -444,7 +444,9 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
if !alreadySeen {
|
||||
item.controllerInteraction.animateDiceSuccess(false, true)
|
||||
Queue.mainQueue().after(1.0) {
|
||||
item.controllerInteraction.animateDiceSuccess(false, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
let colorKeys = ["__allcolors__"]
|
||||
var colors: [String: UIColor] = [:]
|
||||
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(
|
||||
@ -497,8 +497,11 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
micDecoration.isHidden = false
|
||||
micDecoration.startAnimating()
|
||||
|
||||
self.animationView.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
self.animationView.view?.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false)
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
|
||||
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) {
|
||||
@ -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)
|
||||
} else {
|
||||
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)
|
||||
self.animationView.view?.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, removeOnCompletion: false)
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
|
||||
if let layer = self.animationView.view?.layer {
|
||||
transition.updateAlpha(layer: layer, alpha: 1.0)
|
||||
transition.updateTransformScale(layer: layer, scale: 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,31 +78,40 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
params.navigationController?.pushViewController(controller)
|
||||
return true
|
||||
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 isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
|
||||
|
||||
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 }
|
||||
var animateInAsReplacement = false
|
||||
if let navigationController = params.navigationController {
|
||||
for controller in navigationController.overlayControllers {
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitActionAndReplacementAnimation()
|
||||
animateInAsReplacement = true
|
||||
|
||||
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
|
||||
|
||||
var animateInAsReplacement = false
|
||||
if let navigationController = params.navigationController {
|
||||
for controller in navigationController.overlayControllers {
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitActionAndReplacementAnimation()
|
||||
animateInAsReplacement = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch action {
|
||||
case .add:
|
||||
params.navigationController?.presentOverlay(controller: 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: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}))
|
||||
case let .remove(positionInList):
|
||||
params.navigationController?.presentOverlay(controller: 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: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = params.context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}))
|
||||
switch action {
|
||||
case .add:
|
||||
params.navigationController?.presentOverlay(controller: 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: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||
return true
|
||||
}))
|
||||
case let .remove(positionInList):
|
||||
params.navigationController?.presentOverlay(controller: 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: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = params.context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||
}
|
||||
return true
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
params.dismissInput()
|
||||
|
@ -166,21 +166,29 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
case let .stickerPack(name, _):
|
||||
dismissInput()
|
||||
|
||||
let controller = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: .name(name), stickerPacks: [.name(name)], parentNavigationController: navigationController, sendSticker: sendSticker, actionPerformed: { info, items, action in
|
||||
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
|
||||
|
||||
switch action {
|
||||
case .add:
|
||||
present(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
|
||||
}), nil)
|
||||
case let .remove(positionInList):
|
||||
present(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
|
||||
}), nil)
|
||||
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
|
||||
|
||||
switch action {
|
||||
case .add:
|
||||
present(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
|
||||
}), nil)
|
||||
case let .remove(positionInList):
|
||||
present(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
|
||||
}), nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
present(controller, nil)
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "8.8.3",
|
||||
"app": "8.9",
|
||||
"bazel": "5.1.0",
|
||||
"xcode": "13.4.1"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user