mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
1423d5381f
commit
063d39c6be
@ -7957,3 +7957,7 @@ Sorry for the inconvenience.";
|
|||||||
"KeyCommand.ExitFullscreen" = "Exit Fullscreen";
|
"KeyCommand.ExitFullscreen" = "Exit Fullscreen";
|
||||||
|
|
||||||
"StickerPacksSettings.SuggestAnimatedEmoji" = "Suggest Animated Emoji";
|
"StickerPacksSettings.SuggestAnimatedEmoji" = "Suggest Animated Emoji";
|
||||||
|
|
||||||
|
"Emoji.FrequentlyUsed" = "Recently Used";
|
||||||
|
|
||||||
|
"Checkout.PaymentLiabilityBothAlert" = "Telegram will not have access to your credit card information. Credit card details will be handled only by the payment system, {target}.\n\nPayments will go directly to the developer of {target}. Telegram cannot provide any guarantees, so proceed at your own risk. In case of problems, please contact the developer of {target} or your bank.";
|
||||||
|
@ -514,6 +514,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
textInputNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
textInputNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
self.textInputNode = textInputNode
|
self.textInputNode = textInputNode
|
||||||
|
|
||||||
|
textInputNode.textView.inputAssistantItem.leadingBarButtonGroups = []
|
||||||
|
textInputNode.textView.inputAssistantItem.trailingBarButtonGroups = []
|
||||||
|
|
||||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||||
refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||||
textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState)
|
textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState)
|
||||||
|
@ -34,6 +34,7 @@ swift_library(
|
|||||||
"//submodules/SemanticStatusNode:SemanticStatusNode",
|
"//submodules/SemanticStatusNode:SemanticStatusNode",
|
||||||
"//submodules/MoreButtonNode:MoreButtonNode",
|
"//submodules/MoreButtonNode:MoreButtonNode",
|
||||||
"//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent",
|
"//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent",
|
||||||
|
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -16,6 +16,7 @@ import PhotoResources
|
|||||||
import AnimatedStickerComponent
|
import AnimatedStickerComponent
|
||||||
import SemanticStatusNode
|
import SemanticStatusNode
|
||||||
import MediaResources
|
import MediaResources
|
||||||
|
import MultilineTextComponent
|
||||||
|
|
||||||
private let buttonSize = CGSize(width: 88.0, height: 49.0)
|
private let buttonSize = CGSize(width: 88.0, height: 49.0)
|
||||||
private let smallButtonWidth: CGFloat = 69.0
|
private let smallButtonWidth: CGFloat = 69.0
|
||||||
@ -162,7 +163,7 @@ private final class AttachButtonComponent: CombinedComponent {
|
|||||||
static var body: Body {
|
static var body: Body {
|
||||||
let icon = Child(IconComponent.self)
|
let icon = Child(IconComponent.self)
|
||||||
let animatedIcon = Child(AnimatedStickerComponent.self)
|
let animatedIcon = Child(AnimatedStickerComponent.self)
|
||||||
let title = Child(Text.self)
|
let title = Child(MultilineTextComponent.self)
|
||||||
let button = Child(Rectangle.self)
|
let button = Child(Rectangle.self)
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
@ -257,10 +258,15 @@ private final class AttachButtonComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
component: Text(
|
component: MultilineTextComponent(
|
||||||
text: name,
|
text: .plain(NSAttributedString(
|
||||||
|
string: name,
|
||||||
font: Font.regular(10.0),
|
font: Font.regular(10.0),
|
||||||
color: context.component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor
|
textColor: context.component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor,
|
||||||
|
paragraphAlignment: .center)),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
truncationType: .end,
|
||||||
|
maximumNumberOfLines: 1
|
||||||
),
|
),
|
||||||
availableSize: context.availableSize,
|
availableSize: context.availableSize,
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
|
@ -180,6 +180,7 @@ public final class BotCheckoutController: ViewController {
|
|||||||
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.didCancel = true
|
||||||
self.cancelled()
|
self.cancelled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +189,7 @@ public final class BotCheckoutController: ViewController {
|
|||||||
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.didFail = true
|
||||||
self.failed()
|
self.failed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +198,7 @@ public final class BotCheckoutController: ViewController {
|
|||||||
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.didComplete = true
|
||||||
self.completed(currencyValue, receiptMessageId)
|
self.completed(currencyValue, receiptMessageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1299,8 +1299,8 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
}
|
}
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
strongSelf.dismissAnimated()
|
|
||||||
strongSelf.completed(currencyValue, receiptMessageId)
|
strongSelf.completed(currencyValue, receiptMessageId)
|
||||||
|
strongSelf.dismissAnimated()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.dismissAnimated()
|
strongSelf.dismissAnimated()
|
||||||
}
|
}
|
||||||
@ -1464,9 +1464,15 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
if value {
|
if value {
|
||||||
strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true)
|
strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true)
|
||||||
} else {
|
} else {
|
||||||
let paymentText = strongSelf.presentationData.strings.Checkout_PaymentLiabilityAlert
|
let paymentText: String
|
||||||
|
if botPeer.id == providerPeer?.id {
|
||||||
|
paymentText = strongSelf.presentationData.strings.Checkout_PaymentLiabilityBothAlert
|
||||||
|
.replacingOccurrences(of: "{target}", with: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||||
|
} else {
|
||||||
|
paymentText = strongSelf.presentationData.strings.Checkout_PaymentLiabilityAlert
|
||||||
.replacingOccurrences(of: "{target}", with: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder))
|
.replacingOccurrences(of: "{target}", with: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||||
.replacingOccurrences(of: "{payment_system}", with: providerPeer?.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) ?? "")
|
.replacingOccurrences(of: "{payment_system}", with: providerPeer?.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Checkout_LiabilityAlertTitle, text: paymentText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Checkout_LiabilityAlertTitle, text: paymentText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
@ -354,6 +354,16 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
case let .limitExceeded(count, _):
|
case let .limitExceeded(count, _):
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
|
let isPremium = limitsData.0?.isPremium ?? false
|
||||||
|
if isPremium {
|
||||||
|
if case .filter = location {
|
||||||
|
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {})
|
||||||
|
chatListController?.push(controller)
|
||||||
|
} else {
|
||||||
|
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {})
|
||||||
|
chatListController?.push(controller)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if case .filter = location {
|
if case .filter = location {
|
||||||
var replaceImpl: ((ViewController) -> Void)?
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {
|
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {
|
||||||
@ -376,6 +386,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -864,18 +864,35 @@ public final class ChatListNode: ListView {
|
|||||||
break
|
break
|
||||||
case let .limitExceeded(count, _):
|
case let .limitExceeded(count, _):
|
||||||
if isPremium {
|
if isPremium {
|
||||||
|
if case .filter = location {
|
||||||
|
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {})
|
||||||
|
strongSelf.push?(controller)
|
||||||
|
} else {
|
||||||
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {})
|
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {})
|
||||||
strongSelf.push?(controller)
|
strongSelf.push?(controller)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if case .filter = location {
|
||||||
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {
|
||||||
|
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
|
||||||
|
replaceImpl?(premiumScreen)
|
||||||
|
})
|
||||||
|
strongSelf.push?(controller)
|
||||||
|
replaceImpl = { [weak controller] c in
|
||||||
|
controller?.replace(with: c)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var replaceImpl: ((ViewController) -> Void)?
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
|
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
|
||||||
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
|
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
|
||||||
replaceImpl?(premiumScreen)
|
replaceImpl?(premiumScreen)
|
||||||
})
|
})
|
||||||
|
strongSelf.push?(controller)
|
||||||
replaceImpl = { [weak controller] c in
|
replaceImpl = { [weak controller] c in
|
||||||
controller?.replace(with: c)
|
controller?.replace(with: c)
|
||||||
}
|
}
|
||||||
strongSelf.push?(controller)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
|
public func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
|
||||||
switch self {
|
switch self {
|
||||||
case .iPhoneX, .iPhoneXSMax, .iPhoneXr, .iPhone12Mini, .iPhone12, .iPhone12ProMax, .iPhone13Mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax:
|
case .iPhoneX, .iPhoneXSMax, .iPhoneXr, .iPhone12Mini, .iPhone12, .iPhone12ProMax, .iPhone13Mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax:
|
||||||
return inLandscape ? 21.0 : 34.0
|
return inLandscape ? 21.0 : 34.0
|
||||||
|
@ -490,7 +490,16 @@ public class Window1 {
|
|||||||
|
|
||||||
self.keyboardFrameChangeObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil, using: { [weak self] notification in
|
self.keyboardFrameChangeObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil, using: { [weak self] notification in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
var isTablet = false
|
||||||
|
if case .regular = strongSelf.windowLayout.metrics.widthClass {
|
||||||
|
isTablet = true
|
||||||
|
}
|
||||||
|
|
||||||
var keyboardFrame: CGRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect()
|
var keyboardFrame: CGRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect()
|
||||||
|
if isTablet && keyboardFrame.isEmpty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if #available(iOSApplicationExtension 14.2, iOS 14.2, *), UIAccessibility.prefersCrossFadeTransitions {
|
if #available(iOSApplicationExtension 14.2, iOS 14.2, *), UIAccessibility.prefersCrossFadeTransitions {
|
||||||
} else if let keyboardView = strongSelf.statusBarHost?.keyboardView {
|
} else if let keyboardView = strongSelf.statusBarHost?.keyboardView {
|
||||||
if keyboardFrame.width.isEqual(to: keyboardView.bounds.width) && keyboardFrame.height.isEqual(to: keyboardView.bounds.height) && keyboardFrame.minX.isEqual(to: keyboardView.frame.minX) {
|
if keyboardFrame.width.isEqual(to: keyboardView.bounds.width) && keyboardFrame.height.isEqual(to: keyboardView.bounds.height) && keyboardFrame.minX.isEqual(to: keyboardView.frame.minX) {
|
||||||
@ -540,7 +549,11 @@ public class Window1 {
|
|||||||
|
|
||||||
var keyboardHeight: CGFloat
|
var keyboardHeight: CGFloat
|
||||||
if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight {
|
if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight {
|
||||||
|
if isTablet && screenHeight - keyboardFrame.maxY < 5.0 {
|
||||||
|
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||||
|
} else {
|
||||||
keyboardHeight = 0.0
|
keyboardHeight = 0.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||||
if inPopover && !keyboardHeight.isZero {
|
if inPopover && !keyboardHeight.isZero {
|
||||||
@ -1119,7 +1132,7 @@ public class Window1 {
|
|||||||
|
|
||||||
if let image = self.badgeView.image {
|
if let image = self.badgeView.image {
|
||||||
self.updateBadgeVisibility()
|
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: 5.0), size: image.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ swift_library(
|
|||||||
"//submodules/Postbox:Postbox",
|
"//submodules/Postbox:Postbox",
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||||
|
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||||
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -5,6 +5,8 @@ import StoreKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import PersistentStringHash
|
||||||
|
|
||||||
private let productIdentifiers = [
|
private let productIdentifiers = [
|
||||||
"org.telegram.telegramPremium.monthly",
|
"org.telegram.telegramPremium.monthly",
|
||||||
@ -13,6 +15,10 @@ private let productIdentifiers = [
|
|||||||
"org.telegram.telegramPremium.threeMonths"
|
"org.telegram.telegramPremium.threeMonths"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private func isSubscriptionProductId(_ id: String) -> Bool {
|
||||||
|
return id.hasSuffix(".monthly")
|
||||||
|
}
|
||||||
|
|
||||||
private extension NSDecimalNumber {
|
private extension NSDecimalNumber {
|
||||||
func round(_ decimals: Int) -> NSDecimalNumber {
|
func round(_ decimals: Int) -> NSDecimalNumber {
|
||||||
return self.rounding(accordingToBehavior:
|
return self.rounding(accordingToBehavior:
|
||||||
@ -321,6 +327,16 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
case .purchasing:
|
case .purchasing:
|
||||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
||||||
transactionState = .purchasing
|
transactionState = .purchasing
|
||||||
|
if let paymentContext = self.paymentContexts[transaction.payment.productIdentifier] {
|
||||||
|
let _ = updatePendingInAppPurchaseState(
|
||||||
|
engine: self.engine,
|
||||||
|
productId: transaction.payment.productIdentifier,
|
||||||
|
content: PendingInAppPurchaseState(
|
||||||
|
productId: transaction.payment.productIdentifier,
|
||||||
|
targetPeerId: paymentContext.targetPeerId
|
||||||
|
)
|
||||||
|
).start()
|
||||||
|
}
|
||||||
case .deferred:
|
case .deferred:
|
||||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") deferred")
|
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") deferred")
|
||||||
transactionState = .deferred
|
transactionState = .deferred
|
||||||
@ -338,29 +354,52 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ")
|
let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ")
|
||||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]")
|
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]")
|
||||||
|
|
||||||
let transaction = transactionsToAssign.first
|
guard let transaction = transactionsToAssign.first else {
|
||||||
let purposeSignal: Signal<AppStoreTransactionPurpose, NoError>
|
return
|
||||||
if let productIdentifier = transaction?.payment.productIdentifier, let targetPeerId = paymentContexts[productIdentifier]?.targetPeerId {
|
}
|
||||||
purposeSignal = self.availableProducts
|
let productIdentifier = transaction.payment.productIdentifier
|
||||||
|
|
||||||
|
var completion: Signal<Never, NoError> = .never()
|
||||||
|
|
||||||
|
let purpose: Signal<AppStoreTransactionPurpose, NoError>
|
||||||
|
if !isSubscriptionProductId(productIdentifier) {
|
||||||
|
let peerId: Signal<PeerId, NoError>
|
||||||
|
if let targetPeerId = paymentContexts[productIdentifier]?.targetPeerId {
|
||||||
|
peerId = .single(targetPeerId)
|
||||||
|
} else {
|
||||||
|
peerId = pendingInAppPurchaseState(engine: self.engine, productId: productIdentifier)
|
||||||
|
|> mapToSignal { state -> Signal<PeerId, NoError> in
|
||||||
|
if let state = state, let peerId = state.targetPeerId {
|
||||||
|
return .single(peerId)
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completion = updatePendingInAppPurchaseState(engine: self.engine, productId: productIdentifier, content: nil)
|
||||||
|
|
||||||
|
let products = self.availableProducts
|
||||||
|> filter { products in
|
|> filter { products in
|
||||||
return !products.isEmpty
|
return !products.isEmpty
|
||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { products -> AppStoreTransactionPurpose in
|
|
||||||
|
purpose = combineLatest(products, peerId)
|
||||||
|
|> map { products, peerId -> AppStoreTransactionPurpose in
|
||||||
if let product = products.first(where: { $0.id == productIdentifier }) {
|
if let product = products.first(where: { $0.id == productIdentifier }) {
|
||||||
let (currency, amount) = product.priceCurrencyAndAmount
|
let (currency, amount) = product.priceCurrencyAndAmount
|
||||||
return .gift(peerId: targetPeerId, currency: currency, amount: amount)
|
return .gift(peerId: peerId, currency: currency, amount: amount)
|
||||||
} else {
|
} else {
|
||||||
return .gift(peerId: targetPeerId, currency: "", amount: 0)
|
return .gift(peerId: peerId, currency: "", amount: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
purposeSignal = .single(.subscription)
|
purpose = .single(.subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
let receiptData = getReceiptData() ?? Data()
|
let receiptData = getReceiptData() ?? Data()
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
(purposeSignal
|
(purpose
|
||||||
|> castError(AssignAppStoreTransactionError.self)
|
|> castError(AssignAppStoreTransactionError.self)
|
||||||
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose)
|
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose)
|
||||||
@ -379,6 +418,8 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = completion.start()
|
||||||
}),
|
}),
|
||||||
forKey: transactionIds
|
forKey: transactionIds
|
||||||
)
|
)
|
||||||
@ -427,3 +468,50 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class PendingInAppPurchaseState: Codable {
|
||||||
|
public let productId: String
|
||||||
|
public let targetPeerId: PeerId?
|
||||||
|
|
||||||
|
public init(productId: String, targetPeerId: PeerId?) {
|
||||||
|
self.productId = productId
|
||||||
|
self.targetPeerId = targetPeerId
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
self.productId = try container.decode(String.self, forKey: "productId")
|
||||||
|
self.targetPeerId = (try container.decodeIfPresent(Int64.self, forKey: "targetPeerId")).flatMap { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
try container.encode(self.productId, forKey: "productId")
|
||||||
|
if let targetPeerId = self.targetPeerId {
|
||||||
|
try container.encode(targetPeerId.id._internalGetInt64Value(), forKey: "targetPeerId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func pendingInAppPurchaseState(engine: TelegramEngine, productId: String) -> Signal<PendingInAppPurchaseState?, NoError> {
|
||||||
|
let key = ValueBoxKey(length: 8)
|
||||||
|
key.setInt64(0, value: Int64(bitPattern: productId.persistentHashValue))
|
||||||
|
|
||||||
|
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key))
|
||||||
|
|> map { entry -> PendingInAppPurchaseState? in
|
||||||
|
return entry?.get(PendingInAppPurchaseState.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updatePendingInAppPurchaseState(engine: TelegramEngine, productId: String, content: PendingInAppPurchaseState?) -> Signal<Never, NoError> {
|
||||||
|
let key = ValueBoxKey(length: 8)
|
||||||
|
key.setInt64(0, value: Int64(bitPattern: productId.persistentHashValue))
|
||||||
|
|
||||||
|
if let content = content {
|
||||||
|
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key, item: content)
|
||||||
|
} else {
|
||||||
|
return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
//import Foundation
|
|
||||||
//import UIKit
|
|
||||||
//import SwiftSignalKit
|
|
||||||
//import Postbox
|
|
||||||
//import TelegramCore
|
|
||||||
//import TelegramUIPreferences
|
|
||||||
//
|
|
||||||
//final class StoredTransactionState: Codable {
|
|
||||||
// let timestamp: Double
|
|
||||||
// let playbackRate: AudioPlaybackRate
|
|
||||||
//
|
|
||||||
// init(timestamp: Double, playbackRate: AudioPlaybackRate) {
|
|
||||||
// self.timestamp = timestamp
|
|
||||||
// self.playbackRate = playbackRate
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public init(from decoder: Decoder) throws {
|
|
||||||
// let container = try decoder.container(keyedBy: StringCodingKey.self)
|
|
||||||
//
|
|
||||||
// self.timestamp = try container.decode(Double.self, forKey: "timestamp")
|
|
||||||
// self.playbackRate = AudioPlaybackRate(rawValue: try container.decode(Int32.self, forKey: "playbackRate")) ?? .x1
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public func encode(to encoder: Encoder) throws {
|
|
||||||
// var container = encoder.container(keyedBy: StringCodingKey.self)
|
|
||||||
//
|
|
||||||
// try container.encode(self.timestamp, forKey: "timestamp")
|
|
||||||
// try container.encode(self.playbackRate.rawValue, forKey: "playbackRate")
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public func storedState(engine: TelegramEngine, : MessageId) -> Signal<MediaPlaybackStoredState?, NoError> {
|
|
||||||
// let key = ValueBoxKey(length: 20)
|
|
||||||
// key.setInt32(0, value: messageId.namespace)
|
|
||||||
// key.setInt32(4, value: messageId.peerId.namespace._internalGetInt32Value())
|
|
||||||
// key.setInt64(8, value: messageId.peerId.id._internalGetInt64Value())
|
|
||||||
// key.setInt32(16, value: messageId.id)
|
|
||||||
//
|
|
||||||
// return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key))
|
|
||||||
// |> map { entry -> MediaPlaybackStoredState? in
|
|
||||||
// return entry?.get(MediaPlaybackStoredState.self)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public func updateMediaPlaybackStoredStateInteractively(engine: TelegramEngine, messageId: MessageId, state: MediaPlaybackStoredState?) -> Signal<Never, NoError> {
|
|
||||||
// let key = ValueBoxKey(length: 20)
|
|
||||||
// key.setInt32(0, value: messageId.namespace)
|
|
||||||
// key.setInt32(4, value: messageId.peerId.namespace._internalGetInt32Value())
|
|
||||||
// key.setInt64(8, value: messageId.peerId.id._internalGetInt64Value())
|
|
||||||
// key.setInt32(16, value: messageId.id)
|
|
||||||
//
|
|
||||||
// if let state = state {
|
|
||||||
// return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key, item: state)
|
|
||||||
// } else {
|
|
||||||
// return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key)
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -65,6 +65,8 @@ typedef enum
|
|||||||
- (void)setEditButtonsHighlighted:(TGPhotoEditorTab)buttons;
|
- (void)setEditButtonsHighlighted:(TGPhotoEditorTab)buttons;
|
||||||
- (void)setEditButtonsDisabled:(TGPhotoEditorTab)buttons;
|
- (void)setEditButtonsDisabled:(TGPhotoEditorTab)buttons;
|
||||||
|
|
||||||
|
- (void)setAllButtonsHidden:(bool)hidden animated:(bool)animated;
|
||||||
|
|
||||||
@property (nonatomic, readonly) TGPhotoEditorTab currentTabs;
|
@property (nonatomic, readonly) TGPhotoEditorTab currentTabs;
|
||||||
- (void)setToolbarTabs:(TGPhotoEditorTab)tabs animated:(bool)animated;
|
- (void)setToolbarTabs:(TGPhotoEditorTab)tabs animated:(bool)animated;
|
||||||
|
|
||||||
|
@ -333,6 +333,12 @@
|
|||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (keyboardHeight > 0) {
|
||||||
|
[strongSelf->_portraitToolbarView setAllButtonsHidden:true animated:true];
|
||||||
|
} else {
|
||||||
|
[strongSelf->_portraitToolbarView setAllButtonsHidden:false animated:true];
|
||||||
|
}
|
||||||
|
|
||||||
CGFloat offset = 0.0f;
|
CGFloat offset = 0.0f;
|
||||||
if (keyboardHeight > 0)
|
if (keyboardHeight > 0)
|
||||||
offset = -keyboardHeight / 2.0f;
|
offset = -keyboardHeight / 2.0f;
|
||||||
|
@ -239,7 +239,7 @@
|
|||||||
if (_keyboardHeight > 0.0) {
|
if (_keyboardHeight > 0.0) {
|
||||||
backgroundHeight += _keyboardHeight - edgeInsets.bottom;
|
backgroundHeight += _keyboardHeight - edgeInsets.bottom;
|
||||||
}
|
}
|
||||||
_backgroundView.frame = CGRectMake(edgeInsets.left, y, frame.size.width, backgroundHeight);
|
_backgroundView.frame = CGRectMake(edgeInsets.left, y, frame.size.width, backgroundHeight + 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[_backgroundView addSubview:_cancelButton];
|
[_backgroundView addSubview:_cancelButton];
|
||||||
|
|
||||||
|
|
||||||
_doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
|
_doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
|
||||||
_doneButton.exclusiveTouch = true;
|
_doneButton.exclusiveTouch = true;
|
||||||
_doneButton.adjustsImageWhenHighlighted = false;
|
_doneButton.adjustsImageWhenHighlighted = false;
|
||||||
@ -495,6 +494,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setAllButtonsHidden:(bool)hidden animated:(bool)animated
|
||||||
|
{
|
||||||
|
CGFloat targetAlpha = hidden ? 0.0f : 1.0f;
|
||||||
|
|
||||||
|
if (animated)
|
||||||
|
{
|
||||||
|
_buttonsWrapperView.hidden = false;
|
||||||
|
_cancelButton.hidden = false;
|
||||||
|
_doneButton.hidden = false;
|
||||||
|
|
||||||
|
[UIView animateWithDuration:0.2f
|
||||||
|
animations:^
|
||||||
|
{
|
||||||
|
_buttonsWrapperView.alpha = targetAlpha;
|
||||||
|
_cancelButton.alpha = targetAlpha;
|
||||||
|
_doneButton.alpha = targetAlpha;
|
||||||
|
} completion:^(__unused BOOL finished)
|
||||||
|
{
|
||||||
|
_buttonsWrapperView.hidden = hidden;
|
||||||
|
_cancelButton.hidden = hidden;
|
||||||
|
_doneButton.hidden = hidden;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_buttonsWrapperView.alpha = targetAlpha;
|
||||||
|
_cancelButton.alpha = targetAlpha;
|
||||||
|
_doneButton.alpha = targetAlpha;
|
||||||
|
_buttonsWrapperView.hidden = hidden;
|
||||||
|
_cancelButton.hidden = hidden;
|
||||||
|
_doneButton.hidden = hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (TGPhotoEditorButton *)buttonForTab:(TGPhotoEditorTab)tab
|
- (TGPhotoEditorButton *)buttonForTab:(TGPhotoEditorTab)tab
|
||||||
{
|
{
|
||||||
for (TGPhotoEditorButton *button in _buttonsWrapperView.subviews)
|
for (TGPhotoEditorButton *button in _buttonsWrapperView.subviews)
|
||||||
|
@ -287,6 +287,12 @@ public final class LegacyControllerContext: NSObject, LegacyComponentsContext {
|
|||||||
safeInsets.bottom = 21.0
|
safeInsets.bottom = 21.0
|
||||||
} else if validLayout.intrinsicInsets.bottom.isEqual(to: 34.0) {
|
} else if validLayout.intrinsicInsets.bottom.isEqual(to: 34.0) {
|
||||||
safeInsets.bottom = 34.0
|
safeInsets.bottom = 34.0
|
||||||
|
} else {
|
||||||
|
if let knownSafeInset = validLayout.deviceMetrics.onScreenNavigationHeight(inLandscape: validLayout.size.width > validLayout.size.height, systemOnScreenNavigationHeight: nil) {
|
||||||
|
if knownSafeInset > 0.0 {
|
||||||
|
safeInsets.bottom = knownSafeInset
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if controller.navigationPresentation == .modal {
|
if controller.navigationPresentation == .modal {
|
||||||
safeInsets.top = 0.0
|
safeInsets.top = 0.0
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 9.8 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 6.7 KiB |
@ -109,9 +109,6 @@ class GiftAvatarComponent: Component {
|
|||||||
|
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
let panGestureRecoginzer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
|
||||||
self.addGestureRecognizer(panGestureRecoginzer)
|
|
||||||
|
|
||||||
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
|
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
|
||||||
self.addGestureRecognizer(tapGestureRecoginzer)
|
self.addGestureRecognizer(tapGestureRecoginzer)
|
||||||
|
|
||||||
@ -134,58 +131,6 @@ class GiftAvatarComponent: Component {
|
|||||||
self.playAppearanceAnimation(velocity: nil, mirror: false, explode: true)
|
self.playAppearanceAnimation(velocity: nil, mirror: false, explode: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var previousYaw: Float = 0.0
|
|
||||||
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
|
|
||||||
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.previousInteractionTimestamp = CACurrentMediaTime()
|
|
||||||
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
node.removeAnimation(forKey: "rotate", blendOutDuration: 0.1)
|
|
||||||
node.removeAnimation(forKey: "tapRotate", blendOutDuration: 0.1)
|
|
||||||
} else {
|
|
||||||
node.removeAllAnimations()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch gesture.state {
|
|
||||||
case .began:
|
|
||||||
self.previousYaw = 0.0
|
|
||||||
case .changed:
|
|
||||||
let translation = gesture.translation(in: gesture.view)
|
|
||||||
let yawPan = deg2rad(Float(translation.x))
|
|
||||||
|
|
||||||
func rubberBandingOffset(offset: CGFloat, bandingStart: CGFloat) -> CGFloat {
|
|
||||||
let bandedOffset = offset - bandingStart
|
|
||||||
let range: CGFloat = 60.0
|
|
||||||
let coefficient: CGFloat = 0.4
|
|
||||||
return bandingStart + (1.0 - (1.0 / ((bandedOffset * coefficient / range) + 1.0))) * range
|
|
||||||
}
|
|
||||||
|
|
||||||
var pitchTranslation = rubberBandingOffset(offset: abs(translation.y), bandingStart: 0.0)
|
|
||||||
if translation.y < 0.0 {
|
|
||||||
pitchTranslation *= -1.0
|
|
||||||
}
|
|
||||||
let pitchPan = deg2rad(Float(pitchTranslation))
|
|
||||||
|
|
||||||
self.previousYaw = yawPan
|
|
||||||
node.eulerAngles = SCNVector3(pitchPan, yawPan, 0.0)
|
|
||||||
case .ended:
|
|
||||||
let velocity = gesture.velocity(in: gesture.view)
|
|
||||||
|
|
||||||
var smallAngle = false
|
|
||||||
if (self.previousYaw < .pi / 2 && self.previousYaw > -.pi / 2) && abs(velocity.x) < 200 {
|
|
||||||
smallAngle = true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.playAppearanceAnimation(velocity: velocity.x, smallAngle: smallAngle, explode: !smallAngle && abs(velocity.x) > 600)
|
|
||||||
node.eulerAngles = SCNVector3(0.0, 0.0, 0.0)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setup() {
|
private func setup() {
|
||||||
guard let url = getAppBundle().url(forResource: "gift", withExtension: "scn"), let scene = try? SCNScene(url: url, options: nil) else {
|
guard let url = getAppBundle().url(forResource: "gift", withExtension: "scn"), let scene = try? SCNScene(url: url, options: nil) else {
|
||||||
return
|
return
|
||||||
@ -210,6 +155,8 @@ class GiftAvatarComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func onReady() {
|
private func onReady() {
|
||||||
|
self.setupScaleAnimation()
|
||||||
|
|
||||||
self.playAppearanceAnimation(explode: true)
|
self.playAppearanceAnimation(explode: true)
|
||||||
|
|
||||||
self.previousInteractionTimestamp = CACurrentMediaTime()
|
self.previousInteractionTimestamp = CACurrentMediaTime()
|
||||||
@ -224,6 +171,18 @@ class GiftAvatarComponent: Component {
|
|||||||
self.timer?.start()
|
self.timer?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setupScaleAnimation() {
|
||||||
|
let animation = CABasicAnimation(keyPath: "transform.scale")
|
||||||
|
animation.duration = 2.0
|
||||||
|
animation.fromValue = 1.0 //NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1))
|
||||||
|
animation.toValue = 1.15 //NSValue(scnVector3: SCNVector3(x: 0.115, y: 0.115, z: 0.115))
|
||||||
|
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||||
|
animation.autoreverses = true
|
||||||
|
animation.repeatCount = .infinity
|
||||||
|
|
||||||
|
self.avatarNode.view.layer.add(animation, forKey: "scale")
|
||||||
|
}
|
||||||
|
|
||||||
private func playAppearanceAnimation(velocity: CGFloat? = nil, smallAngle: Bool = false, mirror: Bool = false, explode: Bool = false) {
|
private func playAppearanceAnimation(velocity: CGFloat? = nil, smallAngle: Bool = false, mirror: Bool = false, explode: Bool = false) {
|
||||||
guard let scene = self.sceneView.scene else {
|
guard let scene = self.sceneView.scene else {
|
||||||
return
|
return
|
||||||
@ -233,23 +192,50 @@ class GiftAvatarComponent: Component {
|
|||||||
self.previousInteractionTimestamp = currentTime
|
self.previousInteractionTimestamp = currentTime
|
||||||
self.delayTapsTill = currentTime + 0.85
|
self.delayTapsTill = currentTime + 0.85
|
||||||
|
|
||||||
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particles = scene.rootNode.childNode(withName: "particles", recursively: false) {
|
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particlesLeft = scene.rootNode.childNode(withName: "particles_left", recursively: false), let particlesRight = scene.rootNode.childNode(withName: "particles_right", recursively: false), let particlesBottomLeft = scene.rootNode.childNode(withName: "particles_left_bottom", recursively: false), let particlesBottomRight = scene.rootNode.childNode(withName: "particles_right_bottom", recursively: false) {
|
||||||
if let particleSystem = particles.particleSystems?.first {
|
if let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first, let leftBottomParticleSystem = particlesBottomLeft.particleSystems?.first, let rightBottomParticleSystem = particlesBottomRight.particleSystems?.first {
|
||||||
particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.15, 0.3)
|
leftParticleSystem.speedFactor = 2.0
|
||||||
particleSystem.speedFactor = 2.0
|
leftParticleSystem.particleVelocity = 1.6
|
||||||
particleSystem.particleVelocity = 2.2
|
leftParticleSystem.birthRate = 60.0
|
||||||
particleSystem.birthRate = 4.0
|
leftParticleSystem.particleLifeSpan = 4.0
|
||||||
particleSystem.particleLifeSpan = 2.0
|
|
||||||
|
rightParticleSystem.speedFactor = 2.0
|
||||||
|
rightParticleSystem.particleVelocity = 1.6
|
||||||
|
rightParticleSystem.birthRate = 60.0
|
||||||
|
rightParticleSystem.particleLifeSpan = 4.0
|
||||||
|
|
||||||
|
// leftBottomParticleSystem.speedFactor = 2.0
|
||||||
|
leftBottomParticleSystem.particleVelocity = 1.6
|
||||||
|
leftBottomParticleSystem.birthRate = 24.0
|
||||||
|
leftBottomParticleSystem.particleLifeSpan = 7.0
|
||||||
|
|
||||||
|
// rightBottomParticleSystem.speedFactor = 2.0
|
||||||
|
rightBottomParticleSystem.particleVelocity = 1.6
|
||||||
|
rightBottomParticleSystem.birthRate = 24.0
|
||||||
|
rightBottomParticleSystem.particleLifeSpan = 7.0
|
||||||
|
|
||||||
node.physicsField?.isActive = true
|
node.physicsField?.isActive = true
|
||||||
Queue.mainQueue().after(1.0) {
|
Queue.mainQueue().after(1.0) {
|
||||||
node.physicsField?.isActive = false
|
node.physicsField?.isActive = false
|
||||||
particles.particleSystems?.first?.birthRate = 1.2
|
|
||||||
particleSystem.particleVelocity = 1.0
|
|
||||||
particleSystem.particleLifeSpan = 4.0
|
|
||||||
|
|
||||||
let animation = POPBasicAnimation()
|
leftParticleSystem.birthRate = 12.0
|
||||||
animation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
leftParticleSystem.particleVelocity = 1.2
|
||||||
|
leftParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
|
rightParticleSystem.birthRate = 12.0
|
||||||
|
rightParticleSystem.particleVelocity = 1.2
|
||||||
|
rightParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
|
leftBottomParticleSystem.particleVelocity = 1.2
|
||||||
|
leftBottomParticleSystem.birthRate = 7.0
|
||||||
|
leftBottomParticleSystem.particleLifeSpan = 5.0
|
||||||
|
|
||||||
|
rightBottomParticleSystem.particleVelocity = 1.2
|
||||||
|
rightBottomParticleSystem.birthRate = 7.0
|
||||||
|
rightBottomParticleSystem.particleLifeSpan = 5.0
|
||||||
|
|
||||||
|
let leftAnimation = POPBasicAnimation()
|
||||||
|
leftAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||||
property?.readBlock = { particleSystem, values in
|
property?.readBlock = { particleSystem, values in
|
||||||
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
||||||
}
|
}
|
||||||
@ -258,11 +244,27 @@ class GiftAvatarComponent: Component {
|
|||||||
}
|
}
|
||||||
property?.threshold = 0.01
|
property?.threshold = 0.01
|
||||||
}) as! POPAnimatableProperty)
|
}) as! POPAnimatableProperty)
|
||||||
animation.fromValue = 2.0 as NSNumber
|
leftAnimation.fromValue = 1.2 as NSNumber
|
||||||
animation.toValue = 1.0 as NSNumber
|
leftAnimation.toValue = 0.85 as NSNumber
|
||||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
leftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
animation.duration = 0.5
|
leftAnimation.duration = 0.5
|
||||||
particleSystem.pop_add(animation, forKey: "speedFactor")
|
leftParticleSystem.pop_add(leftAnimation, forKey: "speedFactor")
|
||||||
|
|
||||||
|
let rightAnimation = POPBasicAnimation()
|
||||||
|
rightAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||||
|
property?.readBlock = { particleSystem, values in
|
||||||
|
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
||||||
|
}
|
||||||
|
property?.writeBlock = { particleSystem, values in
|
||||||
|
(particleSystem as! SCNParticleSystem).speedFactor = values!.pointee
|
||||||
|
}
|
||||||
|
property?.threshold = 0.01
|
||||||
|
}) as! POPAnimatableProperty)
|
||||||
|
rightAnimation.fromValue = 1.2 as NSNumber
|
||||||
|
rightAnimation.toValue = 0.85 as NSNumber
|
||||||
|
rightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
|
rightAnimation.duration = 0.5
|
||||||
|
rightParticleSystem.pop_add(rightAnimation, forKey: "speedFactor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1441,8 +1441,13 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
starIsVisible = false
|
starIsVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isIntro = true
|
||||||
|
if case .profile = context.component.source {
|
||||||
|
isIntro = false
|
||||||
|
}
|
||||||
|
|
||||||
let star = star.update(
|
let star = star.update(
|
||||||
component: PremiumStarComponent(isVisible: starIsVisible, hasIdleAnimations: state.hasIdleAnimations),
|
component: PremiumStarComponent(isIntro: isIntro, isVisible: starIsVisible, hasIdleAnimations: state.hasIdleAnimations),
|
||||||
availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0),
|
availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
|
@ -46,16 +46,18 @@ private func generateDiffuseTexture() -> UIImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PremiumStarComponent: Component {
|
class PremiumStarComponent: Component {
|
||||||
|
let isIntro: Bool
|
||||||
let isVisible: Bool
|
let isVisible: Bool
|
||||||
let hasIdleAnimations: Bool
|
let hasIdleAnimations: Bool
|
||||||
|
|
||||||
init(isVisible: Bool, hasIdleAnimations: Bool) {
|
init(isIntro: Bool, isVisible: Bool, hasIdleAnimations: Bool) {
|
||||||
|
self.isIntro = isIntro
|
||||||
self.isVisible = isVisible
|
self.isVisible = isVisible
|
||||||
self.hasIdleAnimations = hasIdleAnimations
|
self.hasIdleAnimations = hasIdleAnimations
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: PremiumStarComponent, rhs: PremiumStarComponent) -> Bool {
|
static func ==(lhs: PremiumStarComponent, rhs: PremiumStarComponent) -> Bool {
|
||||||
return lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations
|
return lhs.isIntro == rhs.isIntro && lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations
|
||||||
}
|
}
|
||||||
|
|
||||||
final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView {
|
final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView {
|
||||||
@ -84,7 +86,11 @@ class PremiumStarComponent: Component {
|
|||||||
private var timer: SwiftSignalKit.Timer?
|
private var timer: SwiftSignalKit.Timer?
|
||||||
private var hasIdleAnimations = false
|
private var hasIdleAnimations = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
private let isIntro: Bool
|
||||||
|
|
||||||
|
init(frame: CGRect, isIntro: Bool) {
|
||||||
|
self.isIntro = isIntro
|
||||||
|
|
||||||
self.sceneView = SCNView(frame: CGRect(origin: .zero, size: CGSize(width: 64.0, height: 64.0)))
|
self.sceneView = SCNView(frame: CGRect(origin: .zero, size: CGSize(width: 64.0, height: 64.0)))
|
||||||
self.sceneView.backgroundColor = .clear
|
self.sceneView.backgroundColor = .clear
|
||||||
self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
|
self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
|
||||||
@ -202,15 +208,15 @@ class PremiumStarComponent: Component {
|
|||||||
"rotate",
|
"rotate",
|
||||||
"tapRotate"
|
"tapRotate"
|
||||||
]
|
]
|
||||||
if #available(iOS 11.0, *) {
|
// if #available(iOS 11.0, *) {
|
||||||
for key in keys {
|
// for key in keys {
|
||||||
node.removeAnimation(forKey: key, blendOutDuration: 0.1)
|
// node.removeAnimation(forKey: key, blendOutDuration: 0.1)
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
for key in keys {
|
for key in keys {
|
||||||
node.removeAnimation(forKey: key)
|
node.removeAnimation(forKey: key)
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
|
|
||||||
switch gesture.state {
|
switch gesture.state {
|
||||||
case .began:
|
case .began:
|
||||||
@ -369,10 +375,13 @@ class PremiumStarComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fromScale: Float = self.isIntro ? 0.1 : 0.08
|
||||||
|
let toScale: Float = self.isIntro ? 0.115 : 0.092
|
||||||
|
|
||||||
let animation = CABasicAnimation(keyPath: "scale")
|
let animation = CABasicAnimation(keyPath: "scale")
|
||||||
animation.duration = 2.0
|
animation.duration = 2.0
|
||||||
animation.fromValue = NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1))
|
animation.fromValue = NSValue(scnVector3: SCNVector3(x: fromScale, y: fromScale, z: fromScale))
|
||||||
animation.toValue = NSValue(scnVector3: SCNVector3(x: 0.115, y: 0.115, z: 0.115))
|
animation.toValue = NSValue(scnVector3: SCNVector3(x: toScale, y: toScale, z: toScale))
|
||||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||||
animation.autoreverses = true
|
animation.autoreverses = true
|
||||||
animation.repeatCount = .infinity
|
animation.repeatCount = .infinity
|
||||||
@ -433,30 +442,48 @@ class PremiumStarComponent: Component {
|
|||||||
self.previousInteractionTimestamp = currentTime
|
self.previousInteractionTimestamp = currentTime
|
||||||
self.delayTapsTill = currentTime + 0.85
|
self.delayTapsTill = currentTime + 0.85
|
||||||
|
|
||||||
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particlesLeft = scene.rootNode.childNode(withName: "particles_left", recursively: false), let particlesRight = scene.rootNode.childNode(withName: "particles_right", recursively: false) {
|
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particlesLeft = scene.rootNode.childNode(withName: "particles_left", recursively: false), let particlesRight = scene.rootNode.childNode(withName: "particles_right", recursively: false), let particlesBottomLeft = scene.rootNode.childNode(withName: "particles_left_bottom", recursively: false), let particlesBottomRight = scene.rootNode.childNode(withName: "particles_right_bottom", recursively: false) {
|
||||||
if let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first {
|
if let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first, let leftBottomParticleSystem = particlesBottomLeft.particleSystems?.first, let rightBottomParticleSystem = particlesBottomRight.particleSystems?.first {
|
||||||
leftParticleSystem.speedFactor = 1.3
|
leftParticleSystem.speedFactor = 2.0
|
||||||
leftParticleSystem.particleVelocity = 2.4
|
leftParticleSystem.particleVelocity = 1.6
|
||||||
leftParticleSystem.birthRate = 24.0
|
leftParticleSystem.birthRate = 60.0
|
||||||
leftParticleSystem.particleLifeSpan = 4.0
|
leftParticleSystem.particleLifeSpan = 4.0
|
||||||
|
|
||||||
rightParticleSystem.speedFactor = 1.3
|
rightParticleSystem.speedFactor = 2.0
|
||||||
rightParticleSystem.particleVelocity = 2.4
|
rightParticleSystem.particleVelocity = 1.6
|
||||||
rightParticleSystem.birthRate = 24.0
|
rightParticleSystem.birthRate = 60.0
|
||||||
rightParticleSystem.particleLifeSpan = 4.0
|
rightParticleSystem.particleLifeSpan = 4.0
|
||||||
|
|
||||||
|
// leftBottomParticleSystem.speedFactor = 2.0
|
||||||
|
leftBottomParticleSystem.particleVelocity = 1.6
|
||||||
|
leftBottomParticleSystem.birthRate = 24.0
|
||||||
|
leftBottomParticleSystem.particleLifeSpan = 7.0
|
||||||
|
|
||||||
|
// rightBottomParticleSystem.speedFactor = 2.0
|
||||||
|
rightBottomParticleSystem.particleVelocity = 1.6
|
||||||
|
rightBottomParticleSystem.birthRate = 24.0
|
||||||
|
rightBottomParticleSystem.particleLifeSpan = 7.0
|
||||||
|
|
||||||
node.physicsField?.isActive = true
|
node.physicsField?.isActive = true
|
||||||
Queue.mainQueue().after(1.0) {
|
Queue.mainQueue().after(1.0) {
|
||||||
node.physicsField?.isActive = false
|
node.physicsField?.isActive = false
|
||||||
|
|
||||||
leftParticleSystem.birthRate = 9.0
|
leftParticleSystem.birthRate = 12.0
|
||||||
leftParticleSystem.particleVelocity = 1.2
|
leftParticleSystem.particleVelocity = 1.2
|
||||||
leftParticleSystem.particleLifeSpan = 3.0
|
leftParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
rightParticleSystem.birthRate = 9.0
|
rightParticleSystem.birthRate = 12.0
|
||||||
rightParticleSystem.particleVelocity = 1.2
|
rightParticleSystem.particleVelocity = 1.2
|
||||||
rightParticleSystem.particleLifeSpan = 3.0
|
rightParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
|
leftBottomParticleSystem.particleVelocity = 1.2
|
||||||
|
leftBottomParticleSystem.birthRate = 7.0
|
||||||
|
leftBottomParticleSystem.particleLifeSpan = 5.0
|
||||||
|
|
||||||
|
rightBottomParticleSystem.particleVelocity = 1.2
|
||||||
|
rightBottomParticleSystem.birthRate = 7.0
|
||||||
|
rightBottomParticleSystem.particleLifeSpan = 5.0
|
||||||
|
|
||||||
let leftAnimation = POPBasicAnimation()
|
let leftAnimation = POPBasicAnimation()
|
||||||
leftAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
leftAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||||
property?.readBlock = { particleSystem, values in
|
property?.readBlock = { particleSystem, values in
|
||||||
@ -508,9 +535,9 @@ class PremiumStarComponent: Component {
|
|||||||
let to = SCNVector3(x: 0.0, y: toValue, z: 0.0)
|
let to = SCNVector3(x: 0.0, y: toValue, z: 0.0)
|
||||||
let distance = rad2deg(to.y - from.y)
|
let distance = rad2deg(to.y - from.y)
|
||||||
|
|
||||||
// guard !distance.isZero else {
|
guard !distance.isZero else {
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
let springAnimation = CASpringAnimation(keyPath: "eulerAngles")
|
let springAnimation = CASpringAnimation(keyPath: "eulerAngles")
|
||||||
springAnimation.fromValue = NSValue(scnVector3: from)
|
springAnimation.fromValue = NSValue(scnVector3: from)
|
||||||
@ -541,7 +568,7 @@ class PremiumStarComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeView() -> View {
|
func makeView() -> View {
|
||||||
return View(frame: CGRect())
|
return View(frame: CGRect(), isIntro: self.isIntro)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
@ -123,13 +123,9 @@ public struct ChatListFilterIncludePeers: Equatable, Hashable {
|
|||||||
self.pinnedPeers.insert(peerId, at: 0)
|
self.pinnedPeers.insert(peerId, at: 0)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
if self.peers.count < 100 {
|
|
||||||
self.peers.insert(peerId, at: 0)
|
self.peers.insert(peerId, at: 0)
|
||||||
self.pinnedPeers.insert(peerId, at: 0)
|
self.pinnedPeers.insert(peerId, at: 0)
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,9 +213,6 @@ public struct ChatListFilterData: Equatable, Hashable {
|
|||||||
if self.excludePeers.contains(peerId) {
|
if self.excludePeers.contains(peerId) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if self.excludePeers.count >= 100 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.includePeers.removePeer(peerId)
|
let _ = self.includePeers.removePeer(peerId)
|
||||||
self.excludePeers.append(peerId)
|
self.excludePeers.append(peerId)
|
||||||
|
@ -42,9 +42,6 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio
|
|||||||
additionalCount = 1
|
additionalCount = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let limitCount: Int
|
let limitCount: Int
|
||||||
if case .root = groupId {
|
if case .root = groupId {
|
||||||
limitCount = Int(userLimitsConfiguration.maxPinnedChatCount)
|
limitCount = Int(userLimitsConfiguration.maxPinnedChatCount)
|
||||||
@ -76,8 +73,10 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio
|
|||||||
if updatedData.includePeers.pinnedPeers.contains(peerId) {
|
if updatedData.includePeers.pinnedPeers.contains(peerId) {
|
||||||
updatedData.includePeers.removePinnedPeer(peerId)
|
updatedData.includePeers.removePinnedPeer(peerId)
|
||||||
} else {
|
} else {
|
||||||
if !updatedData.includePeers.addPinnedPeer(peerId) {
|
let _ = updatedData.includePeers.addPinnedPeer(peerId)
|
||||||
|
if updatedData.includePeers.peers.count > userLimitsConfiguration.maxFolderChatsCount {
|
||||||
result = .limitExceeded(count: updatedData.includePeers.peers.count, limit: Int(userLimitsConfiguration.maxFolderChatsCount))
|
result = .limitExceeded(count: updatedData.includePeers.peers.count, limit: Int(userLimitsConfiguration.maxFolderChatsCount))
|
||||||
|
updatedData = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters[index] = .filter(id: id, title: title, emoticon: emoticon, data: updatedData)
|
filters[index] = .filter(id: id, title: title, emoticon: emoticon, data: updatedData)
|
||||||
|
@ -15473,8 +15473,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||||
if actions.contains(3) {
|
if actions.contains(3) {
|
||||||
let context = strongSelf.context
|
let _ = strongSelf.context.engine.messages.deleteAllMessagesWithAuthor(peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud).start()
|
||||||
let _ = context.engine.messages.deleteAllMessagesWithAuthor(peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud).start()
|
|
||||||
let _ = strongSelf.context.engine.messages.clearAuthorHistory(peerId: peerId, memberId: author.id).start()
|
let _ = strongSelf.context.engine.messages.clearAuthorHistory(peerId: peerId, memberId: author.id).start()
|
||||||
} else if actions.contains(0) {
|
} else if actions.contains(0) {
|
||||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start()
|
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start()
|
||||||
|
@ -1482,7 +1482,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
let inputBackgroundInset: CGFloat
|
let inputBackgroundInset: CGFloat
|
||||||
if cleanInsets.bottom < insets.bottom {
|
if cleanInsets.bottom < insets.bottom {
|
||||||
|
if case .regular = layout.metrics.widthClass, insets.bottom < 88.0 {
|
||||||
|
inputBackgroundInset = insets.bottom
|
||||||
|
} else {
|
||||||
inputBackgroundInset = 0.0
|
inputBackgroundInset = 0.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
inputBackgroundInset = cleanInsets.bottom
|
inputBackgroundInset = cleanInsets.bottom
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
|
|||||||
var updatedIconOffset = CGPoint()
|
var updatedIconOffset = CGPoint()
|
||||||
if case .pinnedMessages = subject {
|
if case .pinnedMessages = subject {
|
||||||
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||||
|
updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0)
|
||||||
} else if isReplies {
|
} else if isReplies {
|
||||||
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||||
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
|
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
|
||||||
@ -712,7 +713,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if !alreadySeen {
|
if !alreadySeen {
|
||||||
item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id)
|
item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id)
|
||||||
if let emojiString = self.emojiString, emojiString.count == 1 {
|
if let emojiString = self.emojiString, emojiString.count == 1 {
|
||||||
|
if item.message.id.peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
self.playAdditionalEmojiAnimation(index: 1)
|
self.playAdditionalEmojiAnimation(index: 1)
|
||||||
|
}
|
||||||
} else if let file = file, file.isPremiumSticker {
|
} else if let file = file, file.isPremiumSticker {
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
self.playPremiumStickerAnimation()
|
self.playPremiumStickerAnimation()
|
||||||
@ -974,12 +977,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item.message.forwardInfo != nil || item.message.attributes.first(where: { $0 is ReplyMessageAttribute }) != nil {
|
if item.message.forwardInfo != nil || item.message.attributes.first(where: { $0 is ReplyMessageAttribute }) != nil {
|
||||||
tmpWidth -= 60.0
|
tmpWidth -= 45.0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpWidth -= deliveryFailedInset
|
tmpWidth -= deliveryFailedInset
|
||||||
|
|
||||||
let maximumContentWidth = floor(tmpWidth - layoutConstants.bubble.edgeInset - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - layoutConstants.bubble.contentInsets.right - avatarInset) - 70.0
|
let maximumContentWidth = floor(tmpWidth - layoutConstants.bubble.edgeInset - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - layoutConstants.bubble.contentInsets.right - avatarInset)
|
||||||
|
|
||||||
let font = Font.regular(fontSizeForEmojiString(item.message.text))
|
let font = Font.regular(fontSizeForEmojiString(item.message.text))
|
||||||
let attributedText = stringWithAppliedEntities(item.message.text, entities: item.message.textEntitiesAttribute?.entities ?? [], baseColor: .black, linkColor: .black, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: item.message)
|
let attributedText = stringWithAppliedEntities(item.message.text, entities: item.message.textEntitiesAttribute?.entities ?? [], baseColor: .black, linkColor: .black, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: item.message)
|
||||||
@ -1922,6 +1925,34 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
|
||||||
|
if let item = self.item, let forwardInfo = item.message.forwardInfo {
|
||||||
|
let performAction: () -> Void = {
|
||||||
|
if let sourceMessageId = forwardInfo.sourceMessageId {
|
||||||
|
if !item.message.id.peerId.isReplies, let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
|
||||||
|
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||||
|
} else if case .member = channel.participationStatus {
|
||||||
|
} else {
|
||||||
|
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||||
|
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||||
|
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil)
|
||||||
|
} else if let _ = forwardInfo.authorSignature {
|
||||||
|
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) {
|
||||||
|
return .action({})
|
||||||
|
} else {
|
||||||
|
return .optionalAction(performAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let item = self.item, self.imageNode.frame.contains(location) {
|
if let item = self.item, self.imageNode.frame.contains(location) {
|
||||||
let emojiTapAction: (Bool) -> InternalBubbleTapAction? = { shouldPlay in
|
let emojiTapAction: (Bool) -> InternalBubbleTapAction? = { shouldPlay in
|
||||||
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
||||||
@ -2658,23 +2689,23 @@ private func fontSizeForEmojiString(_ string: String) -> CGFloat {
|
|||||||
case 1:
|
case 1:
|
||||||
multiplier = 1.0
|
multiplier = 1.0
|
||||||
case 2:
|
case 2:
|
||||||
multiplier = 0.7
|
multiplier = 0.84
|
||||||
case 3:
|
case 3:
|
||||||
multiplier = 0.52
|
multiplier = 0.69
|
||||||
case 4:
|
case 4:
|
||||||
multiplier = 0.37
|
multiplier = 0.53
|
||||||
case 5:
|
case 5:
|
||||||
multiplier = 0.28
|
multiplier = 0.46
|
||||||
case 6:
|
case 6:
|
||||||
multiplier = 0.25
|
multiplier = 0.38
|
||||||
case 7:
|
case 7:
|
||||||
multiplier = 0.23
|
multiplier = 0.32
|
||||||
case 8:
|
case 8:
|
||||||
multiplier = 0.21
|
multiplier = 0.27
|
||||||
case 9:
|
case 9:
|
||||||
multiplier = 0.19
|
multiplier = 0.24
|
||||||
default:
|
default:
|
||||||
multiplier = 0.19
|
multiplier = 0.21
|
||||||
}
|
}
|
||||||
return floor(basicSize * multiplier)
|
return floor(basicSize * multiplier)
|
||||||
}
|
}
|
||||||
|
@ -521,6 +521,8 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
|
if !premiumConfiguration.isPremiumDisabled {
|
||||||
let credibilityIconNode: ASImageNode
|
let credibilityIconNode: ASImageNode
|
||||||
if let current = self.credibilityIconNode {
|
if let current = self.credibilityIconNode {
|
||||||
credibilityIconNode = current
|
credibilityIconNode = current
|
||||||
@ -532,6 +534,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
|||||||
self.containerNode.addSubnode(credibilityIconNode)
|
self.containerNode.addSubnode(credibilityIconNode)
|
||||||
}
|
}
|
||||||
credibilityIconNode.frame = CGRect(origin: CGPoint(x: 29.0 - UIScreenPixel, y: 29.0 - UIScreenPixel), size: CGSize(width: 10.0, height: 10.0))
|
credibilityIconNode.frame = CGRect(origin: CGPoint(x: 29.0 - UIScreenPixel, y: 29.0 - UIScreenPixel), size: CGSize(width: 10.0, height: 10.0))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.credibilityIconNode?.removeFromSupernode()
|
self.credibilityIconNode?.removeFromSupernode()
|
||||||
self.credibilityIconNode = nil
|
self.credibilityIconNode = nil
|
||||||
|
@ -213,7 +213,7 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
highlight = false
|
highlight = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let completeString: NSString = completeSourceString.string as NSString
|
let completeString: NSString = (completeSourceString.string.replacingOccurrences(of: "\n", with: " \n")) as NSString
|
||||||
let string = NSMutableAttributedString(string: completeString as String, attributes: [NSAttributedString.Key.foregroundColor: titleColor, NSAttributedString.Key.font: prefixFont])
|
let string = NSMutableAttributedString(string: completeString as String, attributes: [NSAttributedString.Key.foregroundColor: titleColor, NSAttributedString.Key.font: prefixFont])
|
||||||
if highlight, let range = completeSourceString.ranges.first?.range {
|
if highlight, let range = completeSourceString.ranges.first?.range {
|
||||||
string.addAttributes([NSAttributedString.Key.font: peerFont], range: range)
|
string.addAttributes([NSAttributedString.Key.font: peerFont], range: range)
|
||||||
|
@ -443,7 +443,8 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
self.animationNode.playOnce()
|
self.animationNode.playOnce()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !alreadySeen {
|
if !alreadySeen && self.animationNode.isPlaying {
|
||||||
|
item.controllerInteraction.playNextOutgoingGift = false
|
||||||
Queue.mainQueue().after(1.0) {
|
Queue.mainQueue().after(1.0) {
|
||||||
item.controllerInteraction.animateDiceSuccess(false, true)
|
item.controllerInteraction.animateDiceSuccess(false, true)
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
if let buttonResult = self.buttonsContainer.hitTest(point.offsetBy(dx: -self.buttonsContainer.frame.minX, dy: -self.buttonsContainer.frame.minY), with: event) {
|
||||||
|
return buttonResult
|
||||||
|
}
|
||||||
let containerResult = self.contentTextContainer.hitTest(point.offsetBy(dx: -self.contentTextContainer.frame.minX, dy: -self.contentTextContainer.frame.minY), with: event)
|
let containerResult = self.contentTextContainer.hitTest(point.offsetBy(dx: -self.contentTextContainer.frame.minX, dy: -self.contentTextContainer.frame.minY), with: event)
|
||||||
if containerResult?.asyncdisplaykit_node === self.dustNode, self.dustNode?.isRevealed == false {
|
if containerResult?.asyncdisplaykit_node === self.dustNode, self.dustNode?.isRevealed == false {
|
||||||
return containerResult
|
return containerResult
|
||||||
|
@ -647,6 +647,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
var domain: String?
|
var domain: String?
|
||||||
var start: String?
|
var start: String?
|
||||||
var startGroup: String?
|
var startGroup: String?
|
||||||
|
var startChannel: String?
|
||||||
var admin: String?
|
var admin: String?
|
||||||
var game: String?
|
var game: String?
|
||||||
var post: String?
|
var post: String?
|
||||||
@ -684,6 +685,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
voiceChat = ""
|
voiceChat = ""
|
||||||
} else if queryItem.name == "startattach" {
|
} else if queryItem.name == "startattach" {
|
||||||
startAttach = ""
|
startAttach = ""
|
||||||
|
} else if queryItem.name == "startgroup" {
|
||||||
|
startGroup = ""
|
||||||
|
} else if queryItem.name == "startchannel" {
|
||||||
|
startChannel = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -698,7 +703,20 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
if let start = start {
|
if let start = start {
|
||||||
result += "?start=\(start)"
|
result += "?start=\(start)"
|
||||||
} else if let startGroup = startGroup {
|
} else if let startGroup = startGroup {
|
||||||
|
if !startGroup.isEmpty {
|
||||||
result += "?startgroup=\(startGroup)"
|
result += "?startgroup=\(startGroup)"
|
||||||
|
} else {
|
||||||
|
result += "?startgroup"
|
||||||
|
}
|
||||||
|
if let admin = admin {
|
||||||
|
result += "&admin=\(admin)"
|
||||||
|
}
|
||||||
|
} else if let startChannel = startChannel {
|
||||||
|
if !startChannel.isEmpty {
|
||||||
|
result += "?startchannel=\(startChannel)"
|
||||||
|
} else {
|
||||||
|
result += "?startchannel"
|
||||||
|
}
|
||||||
if let admin = admin {
|
if let admin = admin {
|
||||||
result += "&admin=\(admin)"
|
result += "&admin=\(admin)"
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
|||||||
case cachedGeocodes = 4
|
case cachedGeocodes = 4
|
||||||
case visualMediaStoredState = 5
|
case visualMediaStoredState = 5
|
||||||
case cachedImageRecognizedContent = 6
|
case cachedImageRecognizedContent = 6
|
||||||
|
case pendingInAppPurchaseState = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificItemCacheCollectionId {
|
public struct ApplicationSpecificItemCacheCollectionId {
|
||||||
@ -78,6 +79,7 @@ public struct ApplicationSpecificItemCacheCollectionId {
|
|||||||
public static let cachedGeocodes = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedGeocodes.rawValue)
|
public static let cachedGeocodes = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedGeocodes.rawValue)
|
||||||
public static let visualMediaStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.visualMediaStoredState.rawValue)
|
public static let visualMediaStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.visualMediaStoredState.rawValue)
|
||||||
public static let cachedImageRecognizedContent = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedImageRecognizedContent.rawValue)
|
public static let cachedImageRecognizedContent = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedImageRecognizedContent.rawValue)
|
||||||
|
public static let pendingInAppPurchaseState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.pendingInAppPurchaseState.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
||||||
|
@ -230,6 +230,15 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .startAttach(peerName, nil, choose)
|
return .startAttach(peerName, nil, choose)
|
||||||
|
} else if queryItem.name == "startgroup" || queryItem.name == "startchannel" {
|
||||||
|
var botAdminRights: ResolvedBotAdminRights?
|
||||||
|
for queryItem in queryItems {
|
||||||
|
if queryItem.name == "admin", let value = queryItem.value {
|
||||||
|
botAdminRights = ResolvedBotAdminRights(value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .peerName(peerName, .groupBotStart("", botAdminRights))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "8.9",
|
"app": "8.9.1",
|
||||||
"bazel": "5.1.0",
|
"bazel": "5.1.0",
|
||||||
"xcode": "13.4.1"
|
"xcode": "13.4.1"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user