From b5737f0eea9394a3be8842257283cfe91dfb629c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 17 Aug 2022 14:36:14 +0300 Subject: [PATCH 1/5] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 + .../Sources/BotCheckoutControllerNode.swift | 10 +++- .../Sources/StoredTransactionState.swift | 57 ------------------- submodules/TelegramUI/Sources/OpenUrl.swift | 20 ++++++- .../UrlHandling/Sources/UrlHandling.swift | 9 +++ 5 files changed, 38 insertions(+), 60 deletions(-) delete mode 100644 submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 1277b72c59..511914ef1b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7967,3 +7967,5 @@ Sorry for the inconvenience."; "Login.PhoneNumberConfirmation" = "Is this the correct number?"; "Login.Edit" = "Edit"; "Login.Yes" = "Yes"; + +"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."; diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 17c73da3c7..5e33e8e693 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -1464,10 +1464,16 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz if value { strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true) } 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: "{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: { if let strongSelf = self { let _ = ApplicationSpecificNotice.setBotPaymentLiability(accountManager: strongSelf.context.sharedContext.accountManager, peerId: paymentForm.paymentBotId).start() diff --git a/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift b/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift deleted file mode 100644 index 3f0f328d81..0000000000 --- a/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift +++ /dev/null @@ -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 { -// 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 { -// 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) -// } -//} diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 29e075649e..0274b69d66 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -647,6 +647,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur var domain: String? var start: String? var startGroup: String? + var startChannel: String? var admin: String? var game: String? var post: String? @@ -684,6 +685,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur voiceChat = "" } else if queryItem.name == "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 { result += "?start=\(start)" } else if let startGroup = startGroup { - result += "?startgroup=\(startGroup)" + if !startGroup.isEmpty { + 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 { result += "&admin=\(admin)" } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 6845840629..a277e28ba8 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -230,6 +230,15 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { } } 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)) } } } From 27272d8ea9ecb7b55f998a41af730bafd22bc6ff Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 17 Aug 2022 15:20:25 +0300 Subject: [PATCH 2/5] Authorization improvements --- .../Sources/AuthorizationLayout.swift | 18 +- .../TelegramCore/Sources/Authorization.swift | 2 +- ...orizationSequenceCodeEntryController.swift | 22 +- ...ationSequenceCodeEntryControllerNode.swift | 56 ++++- .../AuthorizationSequenceController.swift | 70 ++++++- ...tionSequenceEmailEntryControllerNode.swift | 64 +++++- ...tionSequencePhoneEntryControllerNode.swift | 2 - ...uthorizationSequenceSignUpController.swift | 2 +- ...rizationSequenceSignUpControllerNode.swift | 196 ++++++++++-------- 9 files changed, 328 insertions(+), 104 deletions(-) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationLayout.swift b/submodules/AuthorizationUI/Sources/AuthorizationLayout.swift index 1fddcb52e4..2ed4c8a44f 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationLayout.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationLayout.swift @@ -14,7 +14,8 @@ public struct AuthorizationLayoutItemSpacing { } public struct AuthorizationLayoutItem { - public var node: ASDisplayNode + public var node: ASDisplayNode? + public var view: UIView? public var size: CGSize public var spacingBefore: AuthorizationLayoutItemSpacing public var spacingAfter: AuthorizationLayoutItemSpacing @@ -25,6 +26,14 @@ public struct AuthorizationLayoutItem { self.spacingBefore = spacingBefore self.spacingAfter = spacingAfter } + + + public init(view: UIView, size: CGSize, spacingBefore: AuthorizationLayoutItemSpacing, spacingAfter: AuthorizationLayoutItemSpacing) { + self.view = view + self.size = size + self.spacingBefore = spacingBefore + self.spacingAfter = spacingAfter + } } public final class SolvedAuthorizationLayoutItem { @@ -147,7 +156,12 @@ public func layoutAuthorizationItems(bounds: CGRect, items: [AuthorizationLayout for i in 0 ..< solvedItems.count { let item = solvedItems[i] verticalOrigin += item.spacingBefore! - transition.updateFrame(node: item.item.node, frame: CGRect(origin: CGPoint(x: floor((bounds.size.width - item.item.size.width) / 2.0), y: verticalOrigin), size: item.item.size)) + let itemFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - item.item.size.width) / 2.0), y: verticalOrigin), size: item.item.size) + if let view = item.item.view { + transition.updateFrame(view: view, frame: itemFrame) + } else if let node = item.item.node { + transition.updateFrame(node: node, frame: itemFrame) + } verticalOrigin += item.item.size.height verticalOrigin += item.spacingAfter! } diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 2cd0cea713..0f27e8431a 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -364,7 +364,7 @@ public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> S |> ignoreValues } -public func verifyLoginEmail(account: UnauthorizedAccount, code: AuthorizationCode.EmailVerification) -> Signal { +public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: AuthorizationCode.EmailVerification) -> Signal { return account.postbox.transaction { transaction -> Signal in if let state = transaction.getState() as? UnauthorizedAccountState { switch state.contents { diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift index 25816d8d00..dec21fe7c4 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -16,6 +16,8 @@ final class AuthorizationSequenceCodeEntryController: ViewController { private let openUrl: (String) -> Void var loginWithCode: ((String) -> Void)? + var signInWithApple: (() -> Void)? + var reset: (() -> Void)? var requestNextOption: (() -> Void)? @@ -24,6 +26,8 @@ final class AuthorizationSequenceCodeEntryController: ViewController { private let hapticFeedback = HapticFeedback() + private var appleSignInAllowed = false + var inProgress: Bool = false { didSet { if self.inProgress { @@ -76,6 +80,10 @@ final class AuthorizationSequenceCodeEntryController: ViewController { self?.continueWithCode(code) } + self.controllerNode.signInWithApple = { [weak self] in + self?.signInWithApple?() + } + self.controllerNode.requestNextOption = { [weak self] in self?.requestNextOption?() } @@ -89,7 +97,11 @@ final class AuthorizationSequenceCodeEntryController: ViewController { } if let (number, codeType, nextType, timeout) = self.data { - self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout) + var appleSignInAllowed = false + if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType { + appleSignInAllowed = appleSignInAllowedValue + } + self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed) } } @@ -113,8 +125,14 @@ final class AuthorizationSequenceCodeEntryController: ViewController { self.title = nil } self.data = (number, codeType, nextType, timeout) + + var appleSignInAllowed = false + if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType { + appleSignInAllowed = appleSignInAllowedValue + } + if self.isNodeLoaded { - self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout) + self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed) self.requestLayout(transition: .immediate) } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift index ce6428dd03..d40436a39f 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift @@ -7,6 +7,7 @@ import SwiftSignalKit import TelegramPresentationData import TextFormat import AuthorizationUI +import AuthenticationServices import CodeInputView import PhoneNumberFormat import AnimatedStickerNode @@ -25,6 +26,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF private let nextOptionTitleNode: ImmediateTextNode private let nextOptionButtonNode: HighlightableButtonNode + private let dividerNode: AuthorizationDividerNode + private var signInWithAppleButton: UIControl? + private let codeInputView: CodeInputView private var codeType: SentAuthorizationCodeType? @@ -34,6 +38,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF private var layoutArguments: (ContainerViewLayout, CGFloat)? + private var appleSignInAllowed = false + var phoneNumber: String = "" { didSet { if self.phoneNumber != oldValue { @@ -49,6 +55,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF } var loginWithCode: ((String) -> Void)? + var signInWithApple: (() -> Void)? + var requestNextOption: (() -> Void)? var requestAnotherOption: (() -> Void)? var updateNextEnabled: ((Bool) -> Void)? @@ -106,6 +114,13 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.codeInputView.textField.keyboardType = .numberPad } + self.dividerNode = AuthorizationDividerNode(theme: self.theme, strings: self.strings) + + if #available(iOS 13.0, *) { + self.signInWithAppleButton = ASAuthorizationAppleIDButton(authorizationButtonType: .signIn, authorizationButtonStyle: theme.overallDarkAppearance ? .white : .black) + (self.signInWithAppleButton as? ASAuthorizationAppleIDButton)?.cornerRadius = 11 + } + /*self.codeField = TextFieldNode() self.codeField.textField.font = Font.regular(24.0) self.codeField.textField.textAlignment = .center @@ -140,6 +155,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.addSubnode(self.currentOptionInfoNode) self.addSubnode(self.nextOptionButtonNode) self.addSubnode(self.animationNode) + self.addSubnode(self.dividerNode) self.codeInputView.updated = { [weak self] in guard let strongSelf = self else { @@ -154,12 +170,21 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF //self.codeField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_Code, font: Font.regular(24.0), textColor: self.theme.list.itemPlaceholderTextColor) self.nextOptionButtonNode.addTarget(self, action: #selector(self.nextOptionNodePressed), forControlEvents: .touchUpInside) + self.signInWithAppleButton?.addTarget(self, action: #selector(self.signInWithApplePressed), for: .touchUpInside) } deinit { self.countdownDisposable.dispose() } + override func didLoad() { + super.didLoad() + + if let signInWithAppleButton = self.signInWithAppleButton { + self.view.addSubview(signInWithAppleButton) + } + } + func updateCode(_ code: String) { self.codeInputView.text = code self.textChanged(text: code) @@ -189,10 +214,17 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.codeInputView.text = "" } - func updateData(number: String, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?) { + func updateData(number: String, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, appleSignInAllowed: Bool) { self.codeType = codeType self.phoneNumber = number + var appleSignInAllowed = appleSignInAllowed + if #available(iOS 13.0, *) { + } else { + appleSignInAllowed = false + } + self.appleSignInAllowed = appleSignInAllowed + self.currentOptionNode.attributedText = authorizationCurrentOptionText(codeType, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) if case .missedCall = codeType { self.currentOptionInfoNode.attributedText = NSAttributedString(string: self.strings.Login_CodePhonePatternInfoText, font: Font.regular(16.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) @@ -394,8 +426,22 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) /*items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 88.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))*/ - - items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + + if self.appleSignInAllowed, let signInWithAppleButton = self.signInWithAppleButton { + self.nextOptionButtonNode.isHidden = true + signInWithAppleButton.isHidden = false + + let dividerSize = self.dividerNode.updateLayout(width: layout.size.width) + items.append(AuthorizationLayoutItem(node: self.dividerNode, size: dividerSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + + let buttonSize = CGSize(width: layout.size.width - 48.0, height: 50.0) + items.append(AuthorizationLayoutItem(view: signInWithAppleButton, size: buttonSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + } else { + self.signInWithAppleButton?.isHidden = true + self.dividerNode.isHidden = true + self.nextOptionButtonNode.isHidden = false + items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + } } } else { self.titleIconNode.isHidden = true @@ -476,4 +522,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF @objc func nextOptionNodePressed() { self.requestAnotherOption?() } + + @objc func signInWithApplePressed() { + self.signInWithApple?() + } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift index 445f213585..78b1df7c26 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift @@ -294,7 +294,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail } if case let .email(_, _, _, _, setup) = type, setup, case let .emailVerification(emailCode) = authorizationCode { - strongSelf.actionDisposable.set(((verifyLoginEmail(account: strongSelf.account, code: emailCode)) + strongSelf.actionDisposable.set(((verifyLoginEmailSetup(account: strongSelf.account, code: emailCode)) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { @@ -475,10 +475,28 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() } } + controller.signInWithApple = { [weak self] in + guard let strongSelf = self else { + return + } + + strongSelf.signInWithAppleSetup = false + + if #available(iOS 13.0, *) { + let appleIdProvider = ASAuthorizationAppleIDProvider() + let request = appleIdProvider.createRequest() + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = strongSelf + authorizationController.presentationContextProvider = strongSelf + authorizationController.performRequests() + } + } controller.updateData(number: formatPhoneNumber(number), codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService) return controller } + private var signInWithAppleSetup = false private func emailSetupController(appleSignInAllowed: Bool) -> AuthorizationSequenceEmailEntryController { var currentController: AuthorizationSequenceEmailEntryController? for c in self.viewControllers { @@ -533,6 +551,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail return } + strongSelf.signInWithAppleSetup = true + if #available(iOS 13.0, *) { let appleIdProvider = ASAuthorizationAppleIDProvider() let request = appleIdProvider.createRequest() @@ -559,8 +579,9 @@ public final class AuthorizationSequenceController: NavigationController, MFMail return } - self.actionDisposable.set((verifyLoginEmail(account: self.account, code: .appleToken(token)) - |> deliverOnMainQueue).start(error: { [weak self] error in + if self.signInWithAppleSetup { + self.actionDisposable.set((verifyLoginEmailSetup(account: self.account, code: .appleToken(token)) + |> deliverOnMainQueue).start(error: { [weak self, weak lastController] error in if let strongSelf = self, let lastController = lastController { let text: String switch error { @@ -576,6 +597,49 @@ public final class AuthorizationSequenceController: NavigationController, MFMail lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) + } else { + self.actionDisposable.set( + authorizeWithCode(accountManager: self.sharedContext.accountManager, account: self.account, code: .emailVerification(.appleToken(token)), termsOfService: nil, forcedPasswordSetupNotice: { value in + guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else { + return nil + } + return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry) + }).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } +// lastController?.inProgress = false + switch result { + case let .signUp(data): + let _ = beginSignUp(account: strongSelf.account, data: data).start() + case .loggedIn: + break + } + }, error: { [weak self, weak lastController] error in + Queue.mainQueue().async { + if let strongSelf = self, let lastController = lastController { +// controller.inProgress = false + + let text: String + switch error { + case .limitExceeded: + text = strongSelf.presentationData.strings.Login_CodeFloodError + case .invalidCode: + text = strongSelf.presentationData.strings.Login_InvalidCodeError + case .generic: + text = strongSelf.presentationData.strings.Login_UnknownError + case .codeExpired: + text = strongSelf.presentationData.strings.Login_CodeExpired + let account = strongSelf.account + let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() + } + + lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + } + }) + ) + } default: break } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift index 7c51a5e948..7a9a1bd7e7 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift @@ -9,6 +9,44 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import SolidRoundedButtonNode +final class AuthorizationDividerNode: ASDisplayNode { + private let titleNode: ImmediateTextNode + private let leftLineNode: ASDisplayNode + private let rightLineNode: ASDisplayNode + + init(theme: PresentationTheme, strings: PresentationStrings) { + self.titleNode = ImmediateTextNode() + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.attributedText = NSAttributedString(string: "or", font: Font.regular(17.0), textColor: theme.list.itemSecondaryTextColor) + + self.leftLineNode = ASDisplayNode() + self.leftLineNode.backgroundColor = theme.list.itemSecondaryTextColor + + self.rightLineNode = ASDisplayNode() + self.rightLineNode.backgroundColor = theme.list.itemSecondaryTextColor + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.leftLineNode) + self.addSubnode(self.rightLineNode) + } + + func updateLayout(width: CGFloat) -> CGSize { + let lineSize = CGSize(width: 33.0, height: UIScreenPixel) + let spacing: CGFloat = 7.0 + + let titleSize = self.titleNode.updateLayout(CGSize(width: width - (lineSize.width + spacing) * 2.0, height: .greatestFiniteMagnitude)) + + let height: CGFloat = 40.0 + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - lineSize.width) / 2.0), y: floor((height - titleSize.height) / 2.0)), size: titleSize) + self.titleNode.frame = titleFrame + self.leftLineNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX - spacing - lineSize.width, y: floorToScreenPixels(height / 2.0)), size: lineSize) + self.rightLineNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + spacing, y: floorToScreenPixels(height / 2.0)), size: lineSize) + return CGSize(width: width, height: height) + } +} + final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UITextFieldDelegate { private let strings: PresentationStrings private let theme: PresentationTheme @@ -17,6 +55,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText private let titleNode: ASTextNode private let noticeNode: ASTextNode + private let dividerNode: AuthorizationDividerNode private var signInWithAppleButton: UIControl? private let proceedNode: SolidRoundedButtonNode @@ -82,6 +121,8 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText self.codeField.textField.tintColor = self.theme.list.itemAccentColor self.codeField.textField.placeholder = "Enter Your Email" + self.dividerNode = AuthorizationDividerNode(theme: self.theme, strings: self.strings) + super.init() self.setViewBlock({ @@ -98,6 +139,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText self.addSubnode(self.proceedNode) self.addSubnode(self.noticeNode) self.addSubnode(self.animationNode) + self.addSubnode(self.dividerNode) self.codeField.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged) self.proceedNode.pressed = { [weak self] in @@ -169,17 +211,23 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 32.0, maxValue: 60.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 48.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.proceedNode, size: proceedSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + if let _ = self.signInWithAppleButton, self.appleSignInAllowed { + self.dividerNode.isHidden = false + let dividerSize = self.dividerNode.updateLayout(width: layout.size.width) + items.append(AuthorizationLayoutItem(node: self.dividerNode, size: dividerSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + } else { + self.dividerNode.isHidden = true + } + + items.append(AuthorizationLayoutItem(node: self.proceedNode, size: proceedSize, spacingBefore: self.dividerNode.isHidden ? AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0) : AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 20.0)), items: items, transition: transition, failIfDoesNotFit: false) - if let signInWithAppleButton = self.signInWithAppleButton { - if self.appleSignInAllowed { - signInWithAppleButton.isHidden = false - transition.updateFrame(view: signInWithAppleButton, frame: self.proceedNode.frame) - } else { - signInWithAppleButton.isHidden = true - } + if let signInWithAppleButton = self.signInWithAppleButton, self.appleSignInAllowed { + signInWithAppleButton.isHidden = false + transition.updateFrame(view: signInWithAppleButton, frame: self.proceedNode.frame) + } else { + self.signInWithAppleButton?.isHidden = true } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index a5595b0645..e814836f2f 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -235,8 +235,6 @@ private final class ContactSyncNode: ASDisplayNode { } } - - final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { private let sharedContext: SharedAccountContext private var account: UnauthorizedAccount diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift index 767d4c30f4..c393e46155 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift @@ -50,7 +50,7 @@ final class AuthorizationSequenceSignUpController: ViewController { self.statusBar.statusBarStyle = presentationData.theme.intro.statusBarStyle.style - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) +// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) self.attemptNavigation = { _ in return false diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpControllerNode.swift index 1655c94837..c7b212866f 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpControllerNode.swift @@ -5,6 +5,8 @@ import Display import TelegramPresentationData import TextFormat import Markdown +import AuthorizationUI +import SolidRoundedButtonNode private func roundCorners(diameter: CGFloat) -> UIImage { UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0) @@ -34,6 +36,7 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel private let lastSeparatorNode: ASDisplayNode private let currentPhotoNode: ASImageNode private let addPhotoButton: HighlightableButtonNode + private let proceedNode: SolidRoundedButtonNode private var layoutArguments: (ContainerViewLayout, CGFloat)? @@ -75,20 +78,20 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel self.titleNode = ASTextNode() self.titleNode.isUserInteractionEnabled = false self.titleNode.displaysAsynchronously = false - self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_InfoTitle, font: Font.light(30.0), textColor: theme.list.itemPrimaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_InfoTitle, font: Font.semibold(28.0), textColor: theme.list.itemPrimaryTextColor) self.currentOptionNode = ASTextNode() self.currentOptionNode.isUserInteractionEnabled = false self.currentOptionNode.displaysAsynchronously = false - self.currentOptionNode.attributedText = NSAttributedString(string: self.strings.Login_InfoHelp, font: Font.regular(16.0), textColor: theme.list.itemPlaceholderTextColor, paragraphAlignment: .center) + self.currentOptionNode.attributedText = NSAttributedString(string: self.strings.Login_InfoHelp, font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center) self.termsNode = ImmediateTextNode() self.termsNode.textAlignment = .center self.termsNode.maximumNumberOfLines = 0 self.termsNode.displaysAsynchronously = false - let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor) - let link = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""]) - self.termsNode.attributedText = parseMarkdownIntoAttributedString(strings.Login_TermsOfServiceLabel.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) + let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.list.itemSecondaryTextColor) + let link = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""]) + self.termsNode.attributedText = parseMarkdownIntoAttributedString(strings.Login_TermsOfServiceLabel.replacingOccurrences(of: "\n", with: " ").replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) self.firstSeparatorNode = ASDisplayNode() self.firstSeparatorNode.isLayerBacked = true @@ -132,12 +135,14 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel self.currentPhotoNode.displayWithoutProcessing = true self.addPhotoButton = HighlightableButtonNode() - self.addPhotoButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: self.theme.list.itemPlaceholderTextColor), for: .normal) - self.addPhotoButton.setBackgroundImage(generateCircleImage(diameter: 110.0, lineWidth: 1.0, color: self.theme.list.itemPlaceholderTextColor), for: .normal) - + self.addPhotoButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: self.theme.list.itemAccentColor), for: .normal) + self.addPhotoButton.setBackgroundImage(generateFilledCircleImage(diameter: 110.0, color: self.theme.list.itemAccentColor.withAlphaComponent(0.1), strokeColor: nil, strokeWidth: nil, backgroundColor: nil), for: .normal) + self.addPhotoButton.addSubnode(self.currentPhotoNode) self.addPhotoButton.allowsGroupOpacity = true + self.proceedNode = SolidRoundedButtonNode(title: "Continue", theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false) + super.init() self.setViewBlock({ @@ -158,6 +163,7 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel self.addSubnode(self.termsNode) self.termsNode.isHidden = true self.addSubnode(self.addPhotoButton) + self.addSubnode(self.proceedNode) self.addPhotoButton.addTarget(self, action: #selector(self.addPhotoPressed), forControlEvents: .touchUpInside) @@ -174,6 +180,13 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel self?.openTermsOfService?() } } + + self.proceedNode.pressed = { [weak self] in + if let strongSelf = self { + let name = strongSelf.currentName + strongSelf.signUpWithName?(name.0, name.1) + } + } } func updateData(firstName: String, lastName: String, hasTermsOfService: Bool) { @@ -193,91 +206,110 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel if let inputHeight = layout.inputHeight { insets.bottom += max(inputHeight, layout.standardInputHeight) } - - let availableHeight = max(1.0, layout.size.height - insets.top - insets.bottom) - - if max(layout.size.width, layout.size.height) > 1023.0 { - self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_InfoTitle, font: Font.light(40.0), textColor: self.theme.list.itemPrimaryTextColor) - } else { - self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_InfoTitle, font: Font.light(30.0), textColor: self.theme.list.itemPrimaryTextColor) - } - + + self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_InfoTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor) let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) - let additionalTitleSpacing: CGFloat - if titleSize.width > layout.size.width - 160.0 { - additionalTitleSpacing = 44.0 - } else { - additionalTitleSpacing = 0.0 - } - let minimalTitleSpacing: CGFloat = 10.0 - let maxTitleSpacing: CGFloat = 22.0 - let fieldHeight: CGFloat = 57.0 - let inputFieldsHeight: CGFloat = fieldHeight * 2.0 - let leftInset: CGFloat = 130.0 +// let additionalTitleSpacing: CGFloat +// if titleSize.width > layout.size.width - 160.0 { +// additionalTitleSpacing = 44.0 +// } else { +// additionalTitleSpacing = 0.0 +// } - let minimalNoticeSpacing: CGFloat = 11.0 - let maxNoticeSpacing: CGFloat = 35.0 + let fieldHeight: CGFloat = 54.0 + + let sideInset: CGFloat = 24.0 + let innerInset: CGFloat = 16.0 + +// let minimalNoticeSpacing: CGFloat = 11.0 +// let maxNoticeSpacing: CGFloat = 35.0 let noticeSize = self.currentOptionNode.measure(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude)) let termsSize = self.termsNode.updateLayout(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude)) +// +// let noticeHeight: CGFloat = noticeSize.height + (self.termsNode.isHidden ? 0.0 : (termsSize.height + 4.0)) +// +// let minimalTermsOfServiceSpacing: CGFloat = 6.0 +// let maxTermsOfServiceSpacing: CGFloat = 20.0 +// let minTrailingSpacing: CGFloat = 10.0 +// +// let inputHeight = inputFieldsHeight +// let essentialHeight = additionalTitleSpacing + titleSize.height + minimalTitleSpacing + inputHeight + minimalNoticeSpacing + noticeHeight +// let additionalHeight = minimalTermsOfServiceSpacing + minTrailingSpacing +// +// let navigationHeight: CGFloat +// if essentialHeight + additionalHeight > availableHeight || availableHeight * 0.66 - inputHeight < additionalHeight { +// navigationHeight = min(floor(availableHeight * 0.3), availableHeight - inputFieldsHeight) +// } else { +// navigationHeight = floor(availableHeight * 0.3) +// } +// +// let titleOffset: CGFloat +// if navigationHeight * 0.5 < titleSize.height + minimalTitleSpacing { +// titleOffset = max(navigationBarHeight, floor((navigationHeight - titleSize.height) / 2.0)) +// } else { +// titleOffset = max(navigationBarHeight, max(navigationHeight * 0.5, navigationHeight - maxTitleSpacing - titleSize.height)) +// } +// transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - titleSize.width) / 2.0), y: titleOffset), size: titleSize)) +// +// let avatarSize: CGSize = CGSize(width: 110.0, height: 110.0) +// let addPhotoButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - avatarSize.width) / 2.0), y: navigationHeight + 10.0), size: avatarSize) +// transition.updateFrame(node: self.addPhotoButton, frame: addPhotoButtonFrame) +// self.currentPhotoNode.frame = CGRect(origin: CGPoint(), size: addPhotoButtonFrame.size) +// +// +// +// let firstFieldFrame = CGRect(origin: CGPoint(x: sideInset + innerInset, y: navigationHeight + 3.0), size: CGSize(width: layout.size.width - (sideInset + innerInset) * 2.0, height: fieldHeight)) +// transition.updateFrame(node: self.firstNameField, frame: firstFieldFrame) +// +// let lastFieldFrame = CGRect(origin: CGPoint(x: firstFieldFrame.minX, y: firstFieldFrame.maxY), size: CGSize(width: firstFieldFrame.size.width, height: fieldHeight)) +// transition.updateFrame(node: self.lastNameField, frame: lastFieldFrame) +// +// transition.updateFrame(node: self.firstSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: firstFieldFrame.maxY), size: CGSize(width: layout.size.width - sideInset * 2.0, height: UIScreenPixel))) +// transition.updateFrame(node: self.lastSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: lastFieldFrame.maxY), size: CGSize(width: layout.size.width - sideInset * 2.0, height: UIScreenPixel))) +// +// let additionalAvailableHeight = max(1.0, availableHeight - lastFieldFrame.maxY) +// let additionalAvailableSpacing = max(1.0, additionalAvailableHeight - noticeHeight) +// let noticeSpacingFactor = maxNoticeSpacing / (maxNoticeSpacing + maxTermsOfServiceSpacing + minTrailingSpacing) +// let termsOfServiceSpacingFactor = maxTermsOfServiceSpacing / (maxNoticeSpacing + maxTermsOfServiceSpacing + minTrailingSpacing) +// +// let noticeSpacing: CGFloat +// let termsOfServiceSpacing: CGFloat +// if additionalAvailableHeight <= maxNoticeSpacing + noticeHeight + maxTermsOfServiceSpacing + minTrailingSpacing { +// termsOfServiceSpacing = min(floor(termsOfServiceSpacingFactor * additionalAvailableSpacing), maxTermsOfServiceSpacing) +// noticeSpacing = floor((additionalAvailableHeight - termsOfServiceSpacing - noticeHeight) / 2.0) +// } else { +// noticeSpacing = min(floor(noticeSpacingFactor * additionalAvailableSpacing), maxNoticeSpacing) +// termsOfServiceSpacing = min(floor(termsOfServiceSpacingFactor * additionalAvailableSpacing), maxTermsOfServiceSpacing) +// } +// +// let currentOptionFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - noticeSize.width) / 2.0), y: lastFieldFrame.maxY + max(0.0, noticeSpacing)), size: noticeSize) +// transition.updateFrame(node: self.currentOptionNode, frame: currentOptionFrame) +// let termsFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - termsSize.width) / 2.0), y: layout.size.height - insets.bottom - termsSize.height - 4.0), size: termsSize) +// transition.updateFrame(node: self.termsNode, frame: termsFrame) +// - let noticeHeight: CGFloat = noticeSize.height + (self.termsNode.isHidden ? 0.0 : (termsSize.height + 4.0)) + let avatarSize: CGSize = CGSize(width: 110.0, height: 110.0) + var items: [AuthorizationLayoutItem] = [] + items.append(AuthorizationLayoutItem(node: self.addPhotoButton, size: avatarSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 16.0, maxValue: 16.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + self.currentPhotoNode.frame = CGRect(origin: CGPoint(), size: avatarSize) - let minimalTermsOfServiceSpacing: CGFloat = 6.0 - let maxTermsOfServiceSpacing: CGFloat = 20.0 - let minTrailingSpacing: CGFloat = 10.0 + items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 20.0, maxValue: 20.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - let inputHeight = inputFieldsHeight - let essentialHeight = additionalTitleSpacing + titleSize.height + minimalTitleSpacing + inputHeight + minimalNoticeSpacing + noticeHeight - let additionalHeight = minimalTermsOfServiceSpacing + minTrailingSpacing + items.append(AuthorizationLayoutItem(node: self.firstNameField, size: CGSize(width: layout.size.width - (sideInset + innerInset) * 2.0, height: fieldHeight), spacingBefore: AuthorizationLayoutItemSpacing(weight: 32.0, maxValue: 60.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.firstSeparatorNode, size: CGSize(width: layout.size.width - sideInset * 2.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - let navigationHeight: CGFloat - if essentialHeight + additionalHeight > availableHeight || availableHeight * 0.66 - inputHeight < additionalHeight { - navigationHeight = min(floor(availableHeight * 0.3), availableHeight - inputFieldsHeight) - } else { - navigationHeight = floor(availableHeight * 0.3) - } + items.append(AuthorizationLayoutItem(node: self.lastNameField, size: CGSize(width: layout.size.width - (sideInset + innerInset) * 2.0, height: fieldHeight), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.lastSeparatorNode, size: CGSize(width: layout.size.width - sideInset * 2.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - let titleOffset: CGFloat - if navigationHeight * 0.5 < titleSize.height + minimalTitleSpacing { - titleOffset = max(navigationBarHeight, floor((navigationHeight - titleSize.height) / 2.0)) - } else { - titleOffset = max(navigationBarHeight, max(navigationHeight * 0.5, navigationHeight - maxTitleSpacing - titleSize.height)) - } - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: titleOffset), size: titleSize)) + items.append(AuthorizationLayoutItem(node: self.termsNode, size: termsSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - let addPhotoButtonFrame = CGRect(origin: CGPoint(x: 10.0, y: navigationHeight + 10.0), size: CGSize(width: 110.0, height: 110.0)) - transition.updateFrame(node: self.addPhotoButton, frame: addPhotoButtonFrame) - self.currentPhotoNode.frame = CGRect(origin: CGPoint(), size: addPhotoButtonFrame.size) - - let firstFieldFrame = CGRect(origin: CGPoint(x: leftInset, y: navigationHeight + 3.0), size: CGSize(width: layout.size.width - leftInset, height: fieldHeight)) - transition.updateFrame(node: self.firstNameField, frame: firstFieldFrame) - - let lastFieldFrame = CGRect(origin: CGPoint(x: firstFieldFrame.minX, y: firstFieldFrame.maxY), size: CGSize(width: firstFieldFrame.size.width, height: fieldHeight)) - transition.updateFrame(node: self.lastNameField, frame: lastFieldFrame) - - transition.updateFrame(node: self.firstSeparatorNode, frame: CGRect(origin: CGPoint(x: leftInset, y: firstFieldFrame.maxY), size: CGSize(width: layout.size.width - leftInset, height: UIScreenPixel))) - transition.updateFrame(node: self.lastSeparatorNode, frame: CGRect(origin: CGPoint(x: leftInset, y: lastFieldFrame.maxY), size: CGSize(width: layout.size.width - leftInset, height: UIScreenPixel))) - - let additionalAvailableHeight = max(1.0, availableHeight - lastFieldFrame.maxY) - let additionalAvailableSpacing = max(1.0, additionalAvailableHeight - noticeHeight) - let noticeSpacingFactor = maxNoticeSpacing / (maxNoticeSpacing + maxTermsOfServiceSpacing + minTrailingSpacing) - let termsOfServiceSpacingFactor = maxTermsOfServiceSpacing / (maxNoticeSpacing + maxTermsOfServiceSpacing + minTrailingSpacing) - - let noticeSpacing: CGFloat - let termsOfServiceSpacing: CGFloat - if additionalAvailableHeight <= maxNoticeSpacing + noticeHeight + maxTermsOfServiceSpacing + minTrailingSpacing { - termsOfServiceSpacing = min(floor(termsOfServiceSpacingFactor * additionalAvailableSpacing), maxTermsOfServiceSpacing) - noticeSpacing = floor((additionalAvailableHeight - termsOfServiceSpacing - noticeHeight) / 2.0) - } else { - noticeSpacing = min(floor(noticeSpacingFactor * additionalAvailableSpacing), maxNoticeSpacing) - termsOfServiceSpacing = min(floor(termsOfServiceSpacingFactor * additionalAvailableSpacing), maxTermsOfServiceSpacing) - } - - let currentOptionFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - noticeSize.width) / 2.0), y: lastFieldFrame.maxY + max(0.0, noticeSpacing)), size: noticeSize) - transition.updateFrame(node: self.currentOptionNode, frame: currentOptionFrame) - let termsFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - termsSize.width) / 2.0), y: layout.size.height - insets.bottom - termsSize.height - 4.0), size: termsSize) - transition.updateFrame(node: self.termsNode, frame: termsFrame) + let proceedHeight = self.proceedNode.updateLayout(width: layout.size.width - 48.0, transition: transition) + let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight) + items.append(AuthorizationLayoutItem(node: self.proceedNode, size: proceedSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 20.0, maxValue: 20.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + + let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 20.0)), items: items, transition: transition, failIfDoesNotFit: false) } func activateInput() { From a5399e000508f0ae04b057fef2fa256e72a99418 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 17 Aug 2022 23:37:39 +0300 Subject: [PATCH 3/5] Fix build --- submodules/TelegramUI/Sources/OpenUrl.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 0274b69d66..8ccabf6e46 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -706,7 +706,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur if !startGroup.isEmpty { result += "?startgroup=\(startGroup)" } else { - result += "?startgroup + result += "?startgroup" } if let admin = admin { result += "&admin=\(admin)" @@ -715,7 +715,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur if !startChannel.isEmpty { result += "?startchannel=\(startChannel)" } else { - result += "?startchannel + result += "?startchannel" } if let admin = admin { result += "&admin=\(admin)" From 063d39c6be65ffd8582e6e160dd410e5dbcd3fbe Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 18 Aug 2022 01:57:01 +0300 Subject: [PATCH 4/5] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 + .../AttachmentTextInputPanelNode.swift | 3 + submodules/AttachmentUI/BUILD | 1 + .../Sources/AttachmentPanel.swift | 16 +- .../Sources/BotCheckoutController.swift | 3 + .../Sources/BotCheckoutControllerNode.swift | 12 +- .../ChatListUI/Sources/ChatContextMenus.swift | 45 ++++-- .../Sources/Node/ChatListNode.swift | 39 +++-- submodules/Display/Source/DeviceMetrics.swift | 2 +- submodules/Display/Source/WindowContent.swift | 17 +- submodules/InAppPurchaseManager/BUILD | 2 + .../Sources/InAppPurchaseManager.swift | 106 +++++++++++-- .../Sources/StoredTransactionState.swift | 57 ------- .../LegacyComponents/TGPhotoToolbarView.h | 2 + .../TGMediaPickerGalleryInterfaceView.m | 6 + .../Sources/TGPhotoCaptionInputMixin.m | 2 +- .../Sources/TGPhotoToolbarView.m | 35 ++++- .../LegacyUI/Sources/LegacyController.swift | 6 + submodules/PremiumUI/Resources/emoji.scn | Bin 58408 -> 58392 bytes submodules/PremiumUI/Resources/gift.scn | Bin 30009 -> 52867 bytes submodules/PremiumUI/Resources/smilie.png | Bin 6107 -> 10086 bytes submodules/PremiumUI/Resources/star.scn | Bin 109191 -> 121672 bytes submodules/PremiumUI/Resources/sunglasses.png | Bin 4016 -> 9872 bytes submodules/PremiumUI/Resources/swirl.scn | Bin 16724 -> 16724 bytes submodules/PremiumUI/Resources/thumbsup.png | Bin 2877 -> 6879 bytes .../Sources/GiftAvatarComponent.swift | 148 +++++++++--------- .../Sources/PremiumIntroScreen.swift | 7 +- .../Sources/PremiumStarComponent.swift | 79 +++++++--- .../Peers/ChatListFiltering.swift | 15 +- .../Peers/TogglePeerChatPinned.swift | 7 +- .../TelegramUI/Sources/ChatController.swift | 3 +- .../Sources/ChatControllerNode.swift | 6 +- .../ChatMessageAnimatedStickerItemNode.swift | 57 +++++-- .../Sources/ChatMessageDateHeader.swift | 23 +-- .../Sources/ChatMessageForwardInfoNode.swift | 2 +- .../Sources/ChatMessageGiftItemNode.swift | 3 +- .../ChatPinnedMessageTitlePanelNode.swift | 3 + submodules/TelegramUI/Sources/OpenUrl.swift | 20 ++- .../Sources/PostboxKeys.swift | 2 + .../UrlHandling/Sources/UrlHandling.swift | 9 ++ versions.json | 2 +- 41 files changed, 492 insertions(+), 252 deletions(-) delete mode 100644 submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 679dc94785..26be1b2e9a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7957,3 +7957,7 @@ Sorry for the inconvenience."; "KeyCommand.ExitFullscreen" = "Exit Fullscreen"; "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."; diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 784a68f5c3..02a9daae4c 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -514,6 +514,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS textInputNode.view.disablesInteractiveTransitionGestureRecognizer = true self.textInputNode = textInputNode + textInputNode.textView.inputAssistantItem.leadingBarButtonGroups = [] + textInputNode.textView.inputAssistantItem.trailingBarButtonGroups = [] + if let presentationInterfaceState = self.presentationInterfaceState { refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState) diff --git a/submodules/AttachmentUI/BUILD b/submodules/AttachmentUI/BUILD index d74a51a6ae..5c773e805d 100644 --- a/submodules/AttachmentUI/BUILD +++ b/submodules/AttachmentUI/BUILD @@ -34,6 +34,7 @@ swift_library( "//submodules/SemanticStatusNode:SemanticStatusNode", "//submodules/MoreButtonNode:MoreButtonNode", "//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent", + "//submodules/Components/MultilineTextComponent:MultilineTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index bdc2e56aca..ca9f74c8b7 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -16,6 +16,7 @@ import PhotoResources import AnimatedStickerComponent import SemanticStatusNode import MediaResources +import MultilineTextComponent private let buttonSize = CGSize(width: 88.0, height: 49.0) private let smallButtonWidth: CGFloat = 69.0 @@ -162,7 +163,7 @@ private final class AttachButtonComponent: CombinedComponent { static var body: Body { let icon = Child(IconComponent.self) let animatedIcon = Child(AnimatedStickerComponent.self) - let title = Child(Text.self) + let title = Child(MultilineTextComponent.self) let button = Child(Rectangle.self) return { context in @@ -257,10 +258,15 @@ private final class AttachButtonComponent: CombinedComponent { } let title = title.update( - component: Text( - text: name, - font: Font.regular(10.0), - color: context.component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: name, + 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, transition: .immediate diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift index fad3844aea..990bda668d 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift @@ -180,6 +180,7 @@ public final class BotCheckoutController: ViewController { guard !self.didCancel && !self.didFail && !self.didComplete else { return } + self.didCancel = true self.cancelled() } @@ -188,6 +189,7 @@ public final class BotCheckoutController: ViewController { guard !self.didCancel && !self.didFail && !self.didComplete else { return } + self.didFail = true self.failed() } @@ -196,6 +198,7 @@ public final class BotCheckoutController: ViewController { guard !self.didCancel && !self.didFail && !self.didComplete else { return } + self.didComplete = true self.completed(currencyValue, receiptMessageId) } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 9a0703360e..5e33e8e693 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -1299,8 +1299,8 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } if success { - strongSelf.dismissAnimated() strongSelf.completed(currencyValue, receiptMessageId) + strongSelf.dismissAnimated() } else { strongSelf.dismissAnimated() } @@ -1464,10 +1464,16 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz if value { strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true) } 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: "{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: { if let strongSelf = self { let _ = ApplicationSpecificNotice.setBotPaymentLiability(accountManager: strongSelf.context.sharedContext.accountManager, peerId: paymentForm.paymentBotId).start() diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 2c2e2f66e6..ba3747dd80 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -354,25 +354,36 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch case let .limitExceeded(count, _): f(.default) - 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) - }) - chatListController?.push(controller) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + 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 { - 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) + 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) + }) + chatListController?.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) + }) + chatListController?.push(controller) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } } } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index e78ae7392b..6630b2a315 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -864,18 +864,35 @@ public final class ChatListNode: ListView { break case let .limitExceeded(count, _): if isPremium { - let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {}) - strongSelf.push?(controller) - } 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) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + 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: {}) + 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 { + 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) } } } diff --git a/submodules/Display/Source/DeviceMetrics.swift b/submodules/Display/Source/DeviceMetrics.swift index 4d01d7ac2c..1b35f94e01 100644 --- a/submodules/Display/Source/DeviceMetrics.swift +++ b/submodules/Display/Source/DeviceMetrics.swift @@ -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 { case .iPhoneX, .iPhoneXSMax, .iPhoneXr, .iPhone12Mini, .iPhone12, .iPhone12ProMax, .iPhone13Mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax: return inLandscape ? 21.0 : 34.0 diff --git a/submodules/Display/Source/WindowContent.swift b/submodules/Display/Source/WindowContent.swift index 7a4239e128..186bf3489b 100644 --- a/submodules/Display/Source/WindowContent.swift +++ b/submodules/Display/Source/WindowContent.swift @@ -490,7 +490,16 @@ public class Window1 { self.keyboardFrameChangeObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil, using: { [weak self] notification in 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() + if isTablet && keyboardFrame.isEmpty { + return + } + if #available(iOSApplicationExtension 14.2, iOS 14.2, *), UIAccessibility.prefersCrossFadeTransitions { } 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) { @@ -540,7 +549,11 @@ public class Window1 { var keyboardHeight: CGFloat 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 { keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY) if inPopover && !keyboardHeight.isZero { @@ -1119,7 +1132,7 @@ public class Window1 { if let image = self.badgeView.image { 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) } } } diff --git a/submodules/InAppPurchaseManager/BUILD b/submodules/InAppPurchaseManager/BUILD index 03bc8fd60e..8cbb34b326 100644 --- a/submodules/InAppPurchaseManager/BUILD +++ b/submodules/InAppPurchaseManager/BUILD @@ -14,6 +14,8 @@ swift_library( "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TelegramStringFormatting:TelegramStringFormatting", + "//submodules/TelegramUIPreferences:TelegramUIPreferences", + "//submodules/PersistentStringHash:PersistentStringHash", ], visibility = [ "//visibility:public", diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 03ba28e906..e5072260a7 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -5,6 +5,8 @@ import StoreKit import Postbox import TelegramCore import TelegramStringFormatting +import TelegramUIPreferences +import PersistentStringHash private let productIdentifiers = [ "org.telegram.telegramPremium.monthly", @@ -13,6 +15,10 @@ private let productIdentifiers = [ "org.telegram.telegramPremium.threeMonths" ] +private func isSubscriptionProductId(_ id: String) -> Bool { + return id.hasSuffix(".monthly") +} + private extension NSDecimalNumber { func round(_ decimals: Int) -> NSDecimalNumber { return self.rounding(accordingToBehavior: @@ -321,6 +327,16 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { case .purchasing: Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") 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: Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") deferred") transactionState = .deferred @@ -338,29 +354,52 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ") Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]") - let transaction = transactionsToAssign.first - let purposeSignal: Signal - if let productIdentifier = transaction?.payment.productIdentifier, let targetPeerId = paymentContexts[productIdentifier]?.targetPeerId { - purposeSignal = self.availableProducts + guard let transaction = transactionsToAssign.first else { + return + } + let productIdentifier = transaction.payment.productIdentifier + + var completion: Signal = .never() + + let purpose: Signal + if !isSubscriptionProductId(productIdentifier) { + let peerId: Signal + if let targetPeerId = paymentContexts[productIdentifier]?.targetPeerId { + peerId = .single(targetPeerId) + } else { + peerId = pendingInAppPurchaseState(engine: self.engine, productId: productIdentifier) + |> mapToSignal { state -> Signal 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 return !products.isEmpty } |> take(1) - |> map { products -> AppStoreTransactionPurpose in + + purpose = combineLatest(products, peerId) + |> map { products, peerId -> AppStoreTransactionPurpose in if let product = products.first(where: { $0.id == productIdentifier }) { let (currency, amount) = product.priceCurrencyAndAmount - return .gift(peerId: targetPeerId, currency: currency, amount: amount) + return .gift(peerId: peerId, currency: currency, amount: amount) } else { - return .gift(peerId: targetPeerId, currency: "", amount: 0) + return .gift(peerId: peerId, currency: "", amount: 0) } } } else { - purposeSignal = .single(.subscription) + purpose = .single(.subscription) } let receiptData = getReceiptData() ?? Data() self.disposableSet.set( - (purposeSignal + (purpose |> castError(AssignAppStoreTransactionError.self) |> mapToSignal { purpose -> Signal in self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose) @@ -379,6 +418,8 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { for transaction in transactions { queue.finishTransaction(transaction) } + + let _ = completion.start() }), 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 { + 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 { + 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) + } +} diff --git a/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift b/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift deleted file mode 100644 index 3f0f328d81..0000000000 --- a/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift +++ /dev/null @@ -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 { -// 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 { -// 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) -// } -//} diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h index ee8e04a8db..0d275e10d8 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h @@ -65,6 +65,8 @@ typedef enum - (void)setEditButtonsHighlighted:(TGPhotoEditorTab)buttons; - (void)setEditButtonsDisabled:(TGPhotoEditorTab)buttons; +- (void)setAllButtonsHidden:(bool)hidden animated:(bool)animated; + @property (nonatomic, readonly) TGPhotoEditorTab currentTabs; - (void)setToolbarTabs:(TGPhotoEditorTab)tabs animated:(bool)animated; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index dbc2caa921..12ad1135ac 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -333,6 +333,12 @@ if (strongSelf == nil) return; + if (keyboardHeight > 0) { + [strongSelf->_portraitToolbarView setAllButtonsHidden:true animated:true]; + } else { + [strongSelf->_portraitToolbarView setAllButtonsHidden:false animated:true]; + } + CGFloat offset = 0.0f; if (keyboardHeight > 0) offset = -keyboardHeight / 2.0f; diff --git a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m index cd9460a9ea..4014752b9c 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m +++ b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m @@ -239,7 +239,7 @@ if (_keyboardHeight > 0.0) { 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 diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m b/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m index 77134e7bfe..f652cccb86 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m @@ -54,7 +54,6 @@ [_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_backgroundView addSubview:_cancelButton]; - _doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)]; _doneButton.exclusiveTouch = true; _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 { for (TGPhotoEditorButton *button in _buttonsWrapperView.subviews) diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index a822040022..6cdc49955a 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -287,6 +287,12 @@ public final class LegacyControllerContext: NSObject, LegacyComponentsContext { safeInsets.bottom = 21.0 } else if validLayout.intrinsicInsets.bottom.isEqual(to: 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 { safeInsets.top = 0.0 diff --git a/submodules/PremiumUI/Resources/emoji.scn b/submodules/PremiumUI/Resources/emoji.scn index 26255185bd60fcb423a50190f8110ca5998d01e9..f4f25b3ba7ae752d155bc6a2aae0753f75c3d88e 100644 GIT binary patch delta 15281 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 fb9m6 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 diff --git a/submodules/PremiumUI/Resources/gift.scn b/submodules/PremiumUI/Resources/gift.scn index 14f72e0681d5a22da7076ada94ca112c19c5b84f..45ed4e7c0d43969152b9b066c952095277e8930e 100644 GIT binary patch literal 52867 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 O-RGkJhlE`kd;SjrdsA)z delta 11277 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 diff --git a/submodules/PremiumUI/Resources/smilie.png b/submodules/PremiumUI/Resources/smilie.png index 8de30d88422ec75e8b0da3d3a643c9c03c5500d3..32726e505dbdb45b208efc425c5e006b959d0a00 100644 GIT binary patch literal 10086 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 literal 4016 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 WE~9^I&Z(aO0000OKd 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 b3jqHI^B3_s1oi2V00000NkvXXu0mjfyMibd diff --git a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift index 668a2e6a1f..c49f4f0721 100644 --- a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift +++ b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift @@ -108,10 +108,7 @@ class GiftAvatarComponent: Component { self.addSubview(self.avatarNode.view) self.setup() - - let panGestureRecoginzer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) - self.addGestureRecognizer(panGestureRecoginzer) - + let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))) self.addGestureRecognizer(tapGestureRecoginzer) @@ -134,58 +131,6 @@ class GiftAvatarComponent: Component { 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() { guard let url = getAppBundle().url(forResource: "gift", withExtension: "scn"), let scene = try? SCNScene(url: url, options: nil) else { return @@ -210,6 +155,8 @@ class GiftAvatarComponent: Component { } private func onReady() { + self.setupScaleAnimation() + self.playAppearanceAnimation(explode: true) self.previousInteractionTimestamp = CACurrentMediaTime() @@ -224,6 +171,18 @@ class GiftAvatarComponent: Component { 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) { guard let scene = self.sceneView.scene else { return @@ -233,23 +192,50 @@ class GiftAvatarComponent: Component { self.previousInteractionTimestamp = currentTime 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 let particleSystem = particles.particleSystems?.first { - particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.15, 0.3) - particleSystem.speedFactor = 2.0 - particleSystem.particleVelocity = 2.2 - particleSystem.birthRate = 4.0 - particleSystem.particleLifeSpan = 2.0 + 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, let leftBottomParticleSystem = particlesBottomLeft.particleSystems?.first, let rightBottomParticleSystem = particlesBottomRight.particleSystems?.first { + leftParticleSystem.speedFactor = 2.0 + leftParticleSystem.particleVelocity = 1.6 + leftParticleSystem.birthRate = 60.0 + leftParticleSystem.particleLifeSpan = 4.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 Queue.mainQueue().after(1.0) { node.physicsField?.isActive = false - particles.particleSystems?.first?.birthRate = 1.2 - particleSystem.particleVelocity = 1.0 - particleSystem.particleLifeSpan = 4.0 - let animation = POPBasicAnimation() - animation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in + leftParticleSystem.birthRate = 12.0 + 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 values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor } @@ -258,11 +244,27 @@ class GiftAvatarComponent: Component { } property?.threshold = 0.01 }) as! POPAnimatableProperty) - animation.fromValue = 2.0 as NSNumber - animation.toValue = 1.0 as NSNumber - animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - animation.duration = 0.5 - particleSystem.pop_add(animation, forKey: "speedFactor") + leftAnimation.fromValue = 1.2 as NSNumber + leftAnimation.toValue = 0.85 as NSNumber + leftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + leftAnimation.duration = 0.5 + 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") } } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index b0305e0722..f569953323 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1441,8 +1441,13 @@ private final class PremiumIntroScreenComponent: CombinedComponent { starIsVisible = false } + var isIntro = true + if case .profile = context.component.source { + isIntro = false + } + 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), transition: context.transition ) diff --git a/submodules/PremiumUI/Sources/PremiumStarComponent.swift b/submodules/PremiumUI/Sources/PremiumStarComponent.swift index d679668719..f2948a401c 100644 --- a/submodules/PremiumUI/Sources/PremiumStarComponent.swift +++ b/submodules/PremiumUI/Sources/PremiumStarComponent.swift @@ -19,7 +19,7 @@ private func rad2deg(_ number: Float) -> Float { } private func generateParticlesTexture() -> UIImage { - return UIImage() + return UIImage() } private func generateFlecksTexture() -> UIImage { @@ -46,16 +46,18 @@ private func generateDiffuseTexture() -> UIImage { } class PremiumStarComponent: Component { + let isIntro: Bool let isVisible: Bool let hasIdleAnimations: Bool - init(isVisible: Bool, hasIdleAnimations: Bool) { + init(isIntro: Bool, isVisible: Bool, hasIdleAnimations: Bool) { + self.isIntro = isIntro self.isVisible = isVisible self.hasIdleAnimations = hasIdleAnimations } 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 { @@ -84,7 +86,11 @@ class PremiumStarComponent: Component { private var timer: SwiftSignalKit.Timer? 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.backgroundColor = .clear self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) @@ -202,15 +208,15 @@ class PremiumStarComponent: Component { "rotate", "tapRotate" ] - if #available(iOS 11.0, *) { - for key in keys { - node.removeAnimation(forKey: key, blendOutDuration: 0.1) - } - } else { +// if #available(iOS 11.0, *) { +// for key in keys { +// node.removeAnimation(forKey: key, blendOutDuration: 0.1) +// } +// } else { for key in keys { node.removeAnimation(forKey: key) } - } +// } switch gesture.state { case .began: @@ -369,10 +375,13 @@ class PremiumStarComponent: Component { return } + let fromScale: Float = self.isIntro ? 0.1 : 0.08 + let toScale: Float = self.isIntro ? 0.115 : 0.092 + let animation = CABasicAnimation(keyPath: "scale") animation.duration = 2.0 - animation.fromValue = NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1)) - animation.toValue = NSValue(scnVector3: SCNVector3(x: 0.115, y: 0.115, z: 0.115)) + animation.fromValue = NSValue(scnVector3: SCNVector3(x: fromScale, y: fromScale, z: fromScale)) + animation.toValue = NSValue(scnVector3: SCNVector3(x: toScale, y: toScale, z: toScale)) animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) animation.autoreverses = true animation.repeatCount = .infinity @@ -433,30 +442,48 @@ class PremiumStarComponent: Component { self.previousInteractionTimestamp = currentTime 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 let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first { - leftParticleSystem.speedFactor = 1.3 - leftParticleSystem.particleVelocity = 2.4 - leftParticleSystem.birthRate = 24.0 + 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, let leftBottomParticleSystem = particlesBottomLeft.particleSystems?.first, let rightBottomParticleSystem = particlesBottomRight.particleSystems?.first { + leftParticleSystem.speedFactor = 2.0 + leftParticleSystem.particleVelocity = 1.6 + leftParticleSystem.birthRate = 60.0 leftParticleSystem.particleLifeSpan = 4.0 - rightParticleSystem.speedFactor = 1.3 - rightParticleSystem.particleVelocity = 2.4 - rightParticleSystem.birthRate = 24.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 Queue.mainQueue().after(1.0) { node.physicsField?.isActive = false - leftParticleSystem.birthRate = 9.0 + leftParticleSystem.birthRate = 12.0 leftParticleSystem.particleVelocity = 1.2 leftParticleSystem.particleLifeSpan = 3.0 - rightParticleSystem.birthRate = 9.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 @@ -508,9 +535,9 @@ class PremiumStarComponent: Component { let to = SCNVector3(x: 0.0, y: toValue, z: 0.0) let distance = rad2deg(to.y - from.y) -// guard !distance.isZero else { -// return -// } + guard !distance.isZero else { + return + } let springAnimation = CASpringAnimation(keyPath: "eulerAngles") springAnimation.fromValue = NSValue(scnVector3: from) @@ -541,7 +568,7 @@ class PremiumStarComponent: Component { } func makeView() -> View { - return View(frame: CGRect()) + return View(frame: CGRect(), isIntro: self.isIntro) } func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift index 8afc0dd85f..cdd0e71930 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift @@ -123,13 +123,9 @@ public struct ChatListFilterIncludePeers: Equatable, Hashable { self.pinnedPeers.insert(peerId, at: 0) return true } else { - if self.peers.count < 100 { - self.peers.insert(peerId, at: 0) - self.pinnedPeers.insert(peerId, at: 0) - return true - } else { - return false - } + self.peers.insert(peerId, at: 0) + self.pinnedPeers.insert(peerId, at: 0) + return true } } @@ -217,10 +213,7 @@ public struct ChatListFilterData: Equatable, Hashable { if self.excludePeers.contains(peerId) { return false } - if self.excludePeers.count >= 100 { - return false - } - + let _ = self.includePeers.removePeer(peerId) self.excludePeers.append(peerId) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift index 9b9e204345..9a903484eb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift @@ -42,9 +42,6 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio additionalCount = 1 } - - - let limitCount: Int if case .root = groupId { limitCount = Int(userLimitsConfiguration.maxPinnedChatCount) @@ -76,8 +73,10 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio if updatedData.includePeers.pinnedPeers.contains(peerId) { updatedData.includePeers.removePinnedPeer(peerId) } 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)) + updatedData = data } } filters[index] = .filter(id: id, title: title, emoticon: emoticon, data: updatedData) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9d4514d4a0..faf0be6ecb 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -15473,8 +15473,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) if actions.contains(3) { - let context = strongSelf.context - let _ = context.engine.messages.deleteAllMessagesWithAuthor(peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud).start() + let _ = strongSelf.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() } else if actions.contains(0) { let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start() diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 810a28551a..1243c3130f 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1482,7 +1482,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let inputBackgroundInset: CGFloat 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 { inputBackgroundInset = cleanInsets.bottom } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 0146195542..aef7b66e51 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -109,6 +109,7 @@ class ChatMessageShareButton: HighlightableButtonNode { var updatedIconOffset = CGPoint() if case .pinnedMessages = subject { updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0) } else if isReplies { updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) } else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) { @@ -712,7 +713,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if !alreadySeen { item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id) if let emojiString = self.emojiString, emojiString.count == 1 { - self.playAdditionalEmojiAnimation(index: 1) + if item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + self.playAdditionalEmojiAnimation(index: 1) + } } else if let file = file, file.isPremiumSticker { Queue.mainQueue().after(0.1) { self.playPremiumStickerAnimation() @@ -974,13 +977,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } if item.message.forwardInfo != nil || item.message.attributes.first(where: { $0 is ReplyMessageAttribute }) != nil { - tmpWidth -= 60.0 + tmpWidth -= 45.0 } 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 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) textLayoutAndApply = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maximumContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural)) @@ -1921,6 +1924,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) { let emojiTapAction: (Bool) -> InternalBubbleTapAction? = { shouldPlay in @@ -2658,23 +2689,23 @@ private func fontSizeForEmojiString(_ string: String) -> CGFloat { case 1: multiplier = 1.0 case 2: - multiplier = 0.7 + multiplier = 0.84 case 3: - multiplier = 0.52 + multiplier = 0.69 case 4: - multiplier = 0.37 + multiplier = 0.53 case 5: - multiplier = 0.28 + multiplier = 0.46 case 6: - multiplier = 0.25 + multiplier = 0.38 case 7: - multiplier = 0.23 + multiplier = 0.32 case 8: - multiplier = 0.21 + multiplier = 0.27 case 9: - multiplier = 0.19 + multiplier = 0.24 default: - multiplier = 0.19 + multiplier = 0.21 } return floor(basicSize * multiplier) } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index 65f8555bd5..939444c7a0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -521,17 +521,20 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { } })) - let credibilityIconNode: ASImageNode - if let current = self.credibilityIconNode { - credibilityIconNode = current - } else { - credibilityIconNode = ASImageNode() - credibilityIconNode.displaysAsynchronously = false - credibilityIconNode.displayWithoutProcessing = true - credibilityIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white) - self.containerNode.addSubnode(credibilityIconNode) + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + if !premiumConfiguration.isPremiumDisabled { + let credibilityIconNode: ASImageNode + if let current = self.credibilityIconNode { + credibilityIconNode = current + } else { + credibilityIconNode = ASImageNode() + credibilityIconNode.displaysAsynchronously = false + 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 { self.credibilityIconNode?.removeFromSupernode() self.credibilityIconNode = nil diff --git a/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift index 880b7a3d38..791bfc89b3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift @@ -213,7 +213,7 @@ class ChatMessageForwardInfoNode: ASDisplayNode { 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]) if highlight, let range = completeSourceString.ranges.first?.range { string.addAttributes([NSAttributedString.Key.font: peerFont], range: range) diff --git a/submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift index f0287d959b..73f5a25956 100644 --- a/submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift @@ -443,7 +443,8 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { self.animationNode.playOnce() } - if !alreadySeen { + if !alreadySeen && self.animationNode.isPlaying { + item.controllerInteraction.playNextOutgoingGift = false Queue.mainQueue().after(1.0) { item.controllerInteraction.animateDiceSuccess(false, true) } diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index c07d3aa24c..cb4bcc5e94 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -83,6 +83,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let queue = Queue() 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) if containerResult?.asyncdisplaykit_node === self.dustNode, self.dustNode?.isRevealed == false { return containerResult diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 29e075649e..8ccabf6e46 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -647,6 +647,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur var domain: String? var start: String? var startGroup: String? + var startChannel: String? var admin: String? var game: String? var post: String? @@ -684,6 +685,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur voiceChat = "" } else if queryItem.name == "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 { result += "?start=\(start)" } else if let startGroup = startGroup { - result += "?startgroup=\(startGroup)" + if !startGroup.isEmpty { + 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 { result += "&admin=\(admin)" } diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index 5fde289241..da4427fe3f 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -68,6 +68,7 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { case cachedGeocodes = 4 case visualMediaStoredState = 5 case cachedImageRecognizedContent = 6 + case pendingInAppPurchaseState = 7 } public struct ApplicationSpecificItemCacheCollectionId { @@ -78,6 +79,7 @@ public struct ApplicationSpecificItemCacheCollectionId { public static let cachedGeocodes = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedGeocodes.rawValue) public static let visualMediaStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.visualMediaStoredState.rawValue) public static let cachedImageRecognizedContent = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedImageRecognizedContent.rawValue) + public static let pendingInAppPurchaseState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.pendingInAppPurchaseState.rawValue) } private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 { diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 6845840629..a277e28ba8 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -230,6 +230,15 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { } } 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)) } } } diff --git a/versions.json b/versions.json index 6680658187..a3fafdebf4 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "8.9", + "app": "8.9.1", "bazel": "5.1.0", "xcode": "13.4.1" } From 70b56b9151537a9e13043ed3209894d536a37551 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 18 Aug 2022 03:55:00 +0300 Subject: [PATCH 5/5] Web app installation improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 1 + .../Sources/ChatController.swift | 4 +- .../TelegramUI/Sources/ChatController.swift | 91 +++++++++++-------- .../Sources/NavigateToChatController.swift | 4 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 8 +- .../UrlHandling/Sources/UrlHandling.swift | 4 +- 6 files changed, 64 insertions(+), 48 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 26be1b2e9a..bc403edcca 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7441,6 +7441,7 @@ Sorry for the inconvenience."; "WebApp.AddToAttachmentUnavailableError" = "This bot can't be added to the attachment menu."; "WebApp.AddToAttachmentAlreadyAddedError" = "This bot is already added to your attachment menu."; +"WebApp.AddToAttachmentSucceeded" = "**%@** has been added to your attachment menu."; "WebApp.OpenWebViewAlertTitle" = "Open Web App"; "WebApp.OpenWebViewAlertText" = "**%@** would like to open its web app to proceed.\n\nIt will be able to access your **IP address** and basic device info."; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 764839370a..d56a64bea5 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -151,10 +151,12 @@ public struct ChatControllerInitialBotStart { public struct ChatControllerInitialAttachBotStart { public let botId: PeerId public let payload: String? + public let justInstalled: Bool - public init(botId: PeerId, payload: String?) { + public init(botId: PeerId, payload: String?, justInstalled: Bool) { self.botId = botId self.payload = payload + self.justInstalled = justInstalled } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index faf0be6ecb..542e98c7fb 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9734,7 +9734,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let attachBotStart = self.attachBotStart { self.attachBotStart = nil - self.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload) + self.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } } @@ -11134,12 +11134,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - public func presentAttachmentBot(botId: PeerId, payload: String?) { + public func presentAttachmentBot(botId: PeerId, payload: String?, justInstalled: Bool) { self.attachmentController?.dismiss(animated: true, completion: nil) - self.presentAttachmentMenu(editMediaOptions: nil, editMediaReference: nil, botId: botId, botPayload: payload) + self.presentAttachmentMenu(editMediaOptions: nil, editMediaReference: nil, botId: botId, botPayload: payload, botJustInstalled: justInstalled) } - private func presentAttachmentMenu(editMediaOptions: MessageMediaEditingOptions?, editMediaReference: AnyMediaReference?, botId: PeerId? = nil, botPayload: String? = nil) { + private func presentAttachmentMenu(editMediaOptions: MessageMediaEditingOptions?, editMediaReference: AnyMediaReference?, botId: PeerId? = nil, botPayload: String? = nil, botJustInstalled: Bool = false) { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { return } @@ -11184,33 +11184,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G isScheduledMessages = true } - let buttons: Signal<([AttachmentButtonType], AttachmentButtonType?), NoError> + var peerType: AttachMenuBots.Bot.PeerFlags = [] + if let user = peer as? TelegramUser { + if let _ = user.botInfo { + peerType.insert(.bot) + } else { + peerType.insert(.user) + } + } else if let _ = peer as? TelegramGroup { + peerType = .group + } else if let channel = peer as? TelegramChannel { + if case .broadcast = channel.info { + peerType = .channel + } else { + peerType = .group + } + } + + let buttons: Signal<([AttachmentButtonType], [AttachmentButtonType], AttachmentButtonType?), NoError> if !isScheduledMessages { buttons = self.context.engine.messages.attachMenuBots() |> map { attachMenuBots in var buttons = availableButtons + var allButtons = availableButtons + var initialButton: AttachmentButtonType? if botId == nil { initialButton = .gallery } - var peerType: AttachMenuBots.Bot.PeerFlags = [] - if let user = peer as? TelegramUser { - if let _ = user.botInfo { - peerType.insert(.bot) - } else { - peerType.insert(.user) - } - } else if let _ = peer as? TelegramGroup { - peerType = .group - } else if let channel = peer as? TelegramChannel { - if case .broadcast = channel.info { - peerType = .channel - } else { - peerType = .group - } - } - for bot in attachMenuBots.reversed() { var peerType = peerType if bot.peer.id == peer.id { @@ -11218,19 +11220,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G peerType.remove(.bot) } + let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons) if !bot.peerTypes.intersection(peerType).isEmpty { - let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons) buttons.insert(button, at: 1) if initialButton == nil && bot.peer.id == botId { initialButton = button } } + allButtons.insert(button, at: 1) } - return (buttons, initialButton) + return (buttons, allButtons, initialButton) } } else { - buttons = .single((availableButtons, .gallery)) + buttons = .single((availableButtons, availableButtons, .gallery)) } let dataSettings = self.context.sharedContext.accountManager.transaction { transaction -> GeneratedMediaStoreSettings in @@ -11243,25 +11246,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - let (buttons, initialButton) = buttonsAndInitialButton + let (buttons, allButtons, initialButton) = buttonsAndInitialButton guard let initialButton = initialButton else { if let botId = botId { - let _ = (context.engine.messages.getAttachMenuBot(botId: botId) - |> deliverOnMainQueue).start(next: { bot in - let peer = EnginePeer(bot.peer) - let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, completion: { - let _ = (context.engine.messages.addBotToAttachMenu(botId: botId) - |> deliverOnMainQueue).start(error: { _ in - - }, completed: { - strongSelf.presentAttachmentBot(botId: botId, payload: botPayload) + if let button = allButtons.first(where: { button in + if case let .app(botPeer, _, _) = button, botPeer.id == botId { + return true + } else { + return false + } + }), case let .app(_, botName, _) = button { + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: botJustInstalled ? strongSelf.presentationData.strings.WebApp_AddToAttachmentSucceeded(botName).string : strongSelf.presentationData.strings.WebApp_AddToAttachmentAlreadyAddedError), elevatedLayout: false, action: { _ in return false }), in: .current) + } else { + let _ = (context.engine.messages.getAttachMenuBot(botId: botId) + |> deliverOnMainQueue).start(next: { bot in + let peer = EnginePeer(bot.peer) + let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, completion: { + let _ = (context.engine.messages.addBotToAttachMenu(botId: botId) + |> deliverOnMainQueue).start(error: { _ in + + }, completed: { + strongSelf.presentAttachmentBot(botId: botId, payload: botPayload, justInstalled: true) + }) }) + strongSelf.present(controller, in: .window(.root)) + }, error: { _ in + strongSelf.present(textAlertController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }) - strongSelf.present(controller, in: .window(.root)) - }, error: { _ in - strongSelf.present(textAlertController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }) + } } return } diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index 6fc9b3688e..40909b91ee 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -66,7 +66,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam }) } if let attachBotStart = params.attachBotStart { - controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload) + controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } params.setupController(controller) found = true @@ -85,7 +85,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam }) } if let attachBotStart = params.attachBotStart { - controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload) + controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } } else { controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index aca743fddd..6c32e35454 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -579,13 +579,13 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur if let navigationController = navigationController { let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat)) controller.peerSelected = { peer in - let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: bot.peer.id, payload: payload), useExisting: true)) + let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: bot.peer.id, payload: payload, justInstalled: false), useExisting: true)) } navigationController.pushViewController(controller) } } else { if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext { - let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: peerId, payload: payload), useExisting: true)) + let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: peerId, payload: payload, justInstalled: false), useExisting: true)) } else { presentError(presentationData.strings.WebApp_AddToAttachmentAlreadyAddedError) } @@ -622,13 +622,13 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur if let navigationController = navigationController { let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat)) controller.peerSelected = { peer in - let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload), useExisting: true)) + let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: true), useExisting: true)) } navigationController.pushViewController(controller) } } else { if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext { - let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload), useExisting: true)) + let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: true), useExisting: true)) } } }) diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index a277e28ba8..4baca26a92 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -477,7 +477,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) |> take(1) |> map { botPeer -> ResolvedUrl? in if let botPeer = botPeer?._asPeer() { - return .peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach))) + return .peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach, justInstalled: false))) } else { return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)) } @@ -511,7 +511,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) } |> mapToSignal { botPeer -> Signal in if let botPeer = botPeer { - return .single(.peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload)))) + return .single(.peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: false)))) } else { return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))) }