Merge commit '06d97d2f4d6aecfc5ce41db5dcd7e9c7528b859f'

This commit is contained in:
Ali 2022-08-16 22:19:25 +03:00
commit 29fe0f0879
28 changed files with 312 additions and 88 deletions

View File

@ -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)

View File

@ -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",

View File

@ -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(
font: Font.regular(10.0), string: name,
color: context.component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor font: Font.regular(10.0),
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

View File

@ -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)
} }

View File

@ -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()
} }

View File

@ -354,25 +354,36 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
case let .limitExceeded(count, _): case let .limitExceeded(count, _):
f(.default) f(.default)
if case .filter = location { let isPremium = limitsData.0?.isPremium ?? false
var replaceImpl: ((ViewController) -> Void)? if isPremium {
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: { if case .filter = location {
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats) let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {})
replaceImpl?(premiumScreen) chatListController?.push(controller)
}) } else {
chatListController?.push(controller) let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {})
replaceImpl = { [weak controller] c in chatListController?.push(controller)
controller?.replace(with: c)
} }
} else { } else {
var replaceImpl: ((ViewController) -> Void)? if case .filter = location {
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: { var replaceImpl: ((ViewController) -> Void)?
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats) let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {
replaceImpl?(premiumScreen) let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
}) replaceImpl?(premiumScreen)
chatListController?.push(controller) })
replaceImpl = { [weak controller] c in chatListController?.push(controller)
controller?.replace(with: c) replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
} else {
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
replaceImpl?(premiumScreen)
})
chatListController?.push(controller)
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
} }
} }
} }

View File

@ -864,18 +864,35 @@ public final class ChatListNode: ListView {
break break
case let .limitExceeded(count, _): case let .limitExceeded(count, _):
if isPremium { if isPremium {
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {}) if case .filter = location {
strongSelf.push?(controller) let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: {})
} else { strongSelf.push?(controller)
var replaceImpl: ((ViewController) -> Void)? } else {
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) strongSelf.push?(controller)
replaceImpl?(premiumScreen) }
}) } else {
replaceImpl = { [weak controller] c in if case .filter = location {
controller?.replace(with: c) 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 {
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .pins, 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)
}
} }
strongSelf.push?(controller)
} }
} }
} }

View File

@ -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

View File

@ -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 {
keyboardHeight = 0.0 if isTablet && screenHeight - keyboardFrame.maxY < 5.0 {
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
} else {
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)
} }
} }
} }

View File

@ -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",

View File

@ -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.annual", "org.telegram.telegramPremium.annual",
@ -14,6 +16,10 @@ private let productIdentifiers = [
"org.telegram.telegramPremium.threeMonths" "org.telegram.telegramPremium.threeMonths"
] ]
private func isSubscriptionProductId(_ id: String) -> Bool {
return id.hasSuffix(".monthly") || id.hasSuffix(".annual")
}
private extension NSDecimalNumber { private extension NSDecimalNumber {
func round(_ decimals: Int) -> NSDecimalNumber { func round(_ decimals: Int) -> NSDecimalNumber {
return self.rounding(accordingToBehavior: return self.rounding(accordingToBehavior:
@ -322,6 +328,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
@ -339,29 +355,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)
@ -380,6 +419,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
) )
@ -428,3 +469,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)
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -208,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:
@ -535,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)

View File

@ -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)

View File

@ -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)

View File

@ -15537,8 +15537,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()

View File

@ -1482,7 +1482,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
let inputBackgroundInset: CGFloat let inputBackgroundInset: CGFloat
if cleanInsets.bottom < insets.bottom { if cleanInsets.bottom < insets.bottom {
inputBackgroundInset = 0.0 if case .regular = layout.metrics.widthClass, insets.bottom < 88.0 {
inputBackgroundInset = insets.bottom
} else {
inputBackgroundInset = 0.0
}
} else { } else {
inputBackgroundInset = cleanInsets.bottom inputBackgroundInset = cleanInsets.bottom
} }

View File

@ -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) {
@ -976,7 +977,7 @@ 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
@ -1924,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]

View File

@ -521,17 +521,20 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
} }
})) }))
let credibilityIconNode: ASImageNode let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
if let current = self.credibilityIconNode { if !premiumConfiguration.isPremiumDisabled {
credibilityIconNode = current let credibilityIconNode: ASImageNode
} else { if let current = self.credibilityIconNode {
credibilityIconNode = ASImageNode() credibilityIconNode = current
credibilityIconNode.displaysAsynchronously = false } else {
credibilityIconNode.displayWithoutProcessing = true credibilityIconNode = ASImageNode()
credibilityIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white) credibilityIconNode.displaysAsynchronously = false
self.containerNode.addSubnode(credibilityIconNode) credibilityIconNode.displayWithoutProcessing = true
credibilityIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
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

View File

@ -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)

View File

@ -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)
} }

View File

@ -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

View File

@ -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 {