mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-06-20 18:24:43 +00:00
Merge commit '319c164369f57b4996942de6c3f950998a543e2f'
This commit is contained in:
@@ -7962,6 +7962,7 @@ Sorry for the inconvenience.";
|
||||
"Emoji.FrequentlyUsed" = "Recently Used";
|
||||
|
||||
"Premium.Annual" = "Annual";
|
||||
"Premium.Semiannual" = "Semiannual";
|
||||
"Premium.Monthly" = "Monthly";
|
||||
|
||||
"Login.PhoneNumberConfirmation" = "Is this the correct number?";
|
||||
|
||||
@@ -11,6 +11,8 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode
|
||||
}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let strings: PresentationStrings
|
||||
private let theme: PresentationTheme
|
||||
private let openUrl: (String) -> Void
|
||||
@@ -30,12 +32,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
|
||||
public var inProgress: Bool = false {
|
||||
didSet {
|
||||
// if self.inProgress {
|
||||
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor))
|
||||
// self.navigationItem.rightBarButtonItem = item
|
||||
// } else {
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
// }
|
||||
self.updateNavigationItems()
|
||||
self.controllerNode.inProgress = self.inProgress
|
||||
}
|
||||
}
|
||||
@@ -52,9 +49,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
self.hasActiveInput = true
|
||||
|
||||
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
|
||||
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
|
||||
|
||||
self.attemptNavigation = { _ in
|
||||
return false
|
||||
}
|
||||
@@ -124,6 +119,19 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
self.controllerNode.animateError(text: text)
|
||||
}
|
||||
|
||||
func updateNavigationItems() {
|
||||
guard let layout = self.validLayout, layout.size.width < 360.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
}
|
||||
|
||||
public func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) {
|
||||
self.termsOfService = termsOfService
|
||||
if self.data?.0 != number || self.data?.1 != email || self.data?.2 != codeType || self.data?.3 != nextType || self.data?.4 != timeout {
|
||||
@@ -144,6 +152,13 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let hadLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
if !hadLayout {
|
||||
self.updateNavigationItems()
|
||||
}
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +249,9 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
controller.present(strongSelf.sharedContext.makeProxySettingsController(sharedContext: strongSelf.sharedContext, account: strongSelf.account), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}))
|
||||
}
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), in: .window(.root))
|
||||
(controller.navigationController as? NavigationController)?.presentOverlay(controller: standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true)
|
||||
|
||||
controller.dismissConfirmation()
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -549,6 +551,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
|
||||
controller?.inProgress = true
|
||||
|
||||
strongSelf.currentEmail = email
|
||||
|
||||
strongSelf.actionDisposable.set((sendLoginEmailCode(account: strongSelf.account, email: email)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
if let strongSelf = self, let controller = controller {
|
||||
@@ -570,12 +574,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
|
||||
controller.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))
|
||||
}
|
||||
}, completed: { [weak self] in
|
||||
}, completed: {
|
||||
controller?.inProgress = false
|
||||
|
||||
if let strongSelf = self {
|
||||
strongSelf.currentEmail = email
|
||||
}
|
||||
}))
|
||||
}
|
||||
controller.signInWithApple = { [weak self] in
|
||||
@@ -645,7 +645,6 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
// lastController?.inProgress = false
|
||||
switch result {
|
||||
case let .signUp(data):
|
||||
let _ = beginSignUp(account: strongSelf.account, data: data).start()
|
||||
@@ -655,8 +654,6 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
}, 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:
|
||||
|
||||
@@ -17,6 +17,8 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||
return self.displayNode as! AuthorizationSequenceEmailEntryControllerNode
|
||||
}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let presentationData: PresentationData
|
||||
|
||||
public var proceedWithEmail: ((String) -> Void)?
|
||||
@@ -28,12 +30,7 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||
|
||||
public var inProgress: Bool = false {
|
||||
didSet {
|
||||
// if self.inProgress {
|
||||
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
// self.navigationItem.rightBarButtonItem = item
|
||||
// } else {
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
// }
|
||||
self.updateNavigationItems()
|
||||
self.controllerNode.inProgress = self.inProgress
|
||||
}
|
||||
}
|
||||
@@ -56,8 +53,6 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||
self.navigationBar?.backPressed = {
|
||||
back()
|
||||
}
|
||||
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@@ -87,6 +82,19 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||
self.controllerNode.activateInput()
|
||||
}
|
||||
|
||||
func updateNavigationItems() {
|
||||
guard let layout = self.validLayout, layout.size.width < 360.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
}
|
||||
|
||||
public func updateData(appleSignInAllowed: Bool) {
|
||||
var appleSignInAllowed = appleSignInAllowed
|
||||
if #available(iOS 13.0, *) {
|
||||
@@ -104,6 +112,13 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let hadLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
if !hadLayout {
|
||||
self.updateNavigationItems()
|
||||
}
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
|
||||
+23
-5
@@ -77,6 +77,14 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
var inProgress: Bool = false {
|
||||
didSet {
|
||||
self.codeField.alpha = self.inProgress ? 0.6 : 1.0
|
||||
|
||||
if self.inProgress != oldValue {
|
||||
if self.inProgress {
|
||||
self.proceedNode.transitionToProgress()
|
||||
} else {
|
||||
self.proceedNode.transitionFromProgress()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +95,6 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
@@ -189,11 +196,12 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
|
||||
var insets = layout.insets(options: [])
|
||||
insets.top = layout.statusBarHeight ?? 20.0
|
||||
|
||||
if let inputHeight = layout.inputHeight {
|
||||
insets.bottom = max(inputHeight, insets.bottom)
|
||||
}
|
||||
|
||||
let titleInset: CGFloat = layout.size.width > 320.0 ? 18.0 : 0.0
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.mode == .setup ? self.strings.Login_AddEmailTitle : self.strings.Login_EnterNewEmailTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
|
||||
let animationSize = CGSize(width: 100.0, height: 100.0)
|
||||
@@ -204,15 +212,25 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight)
|
||||
|
||||
var items: [AuthorizationLayoutItem] = []
|
||||
items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
|
||||
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.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: titleInset, maxValue: titleInset), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.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: 22.0, maxValue: 40.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)))
|
||||
|
||||
if layout.size.width > 320.0 {
|
||||
items.insert(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), at: 0)
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
self.proceedNode.isHidden = false
|
||||
self.animationNode.isHidden = false
|
||||
self.animationNode.visibility = true
|
||||
} else {
|
||||
insets.top = navigationBarHeight
|
||||
self.proceedNode.isHidden = true
|
||||
self.animationNode.isHidden = true
|
||||
}
|
||||
|
||||
let inset: CGFloat = 24.0
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - proceedSize.width) / 2.0), y: layout.size.height - insets.bottom - proceedSize.height - inset), size: proceedSize)
|
||||
transition.updateFrame(node: self.proceedNode, frame: buttonFrame)
|
||||
|
||||
+46
-14
@@ -17,6 +17,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
return self.displayNode as! AuthorizationSequencePhoneEntryControllerNode
|
||||
}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private var account: UnauthorizedAccount
|
||||
private let isTestingEnvironment: Bool
|
||||
@@ -43,12 +45,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
|
||||
var inProgress: Bool = false {
|
||||
didSet {
|
||||
// if self.inProgress {
|
||||
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
// self.navigationItem.rightBarButtonItem = item
|
||||
// } else {
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
// }
|
||||
self.updateNavigationItems()
|
||||
self.controllerNode.inProgress = self.inProgress
|
||||
self.confirmationController?.inProgress = self.inProgress
|
||||
}
|
||||
@@ -89,7 +86,6 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
if !otherAccountPhoneNumbers.1.isEmpty {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
}
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@@ -104,6 +100,19 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
self.back()
|
||||
}
|
||||
|
||||
func updateNavigationItems() {
|
||||
guard let layout = self.validLayout, layout.size.width < 360.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
}
|
||||
|
||||
func updateData(countryCode: Int32, countryName: String?, number: String) {
|
||||
self.currentData = (countryCode, countryName, number)
|
||||
if self.isNodeLoaded {
|
||||
@@ -207,6 +216,13 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let hadLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
if !hadLayout {
|
||||
self.updateNavigationItems()
|
||||
}
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
|
||||
if self.shouldAnimateIn, let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
||||
@@ -217,6 +233,11 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func dismissConfirmation() {
|
||||
self.confirmationController?.dismissAnimated()
|
||||
self.confirmationController = nil
|
||||
}
|
||||
|
||||
@objc func nextPressed() {
|
||||
let (_, _, number) = self.controllerNode.codeAndNumber
|
||||
if !number.isEmpty {
|
||||
@@ -239,16 +260,27 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}))
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
|
||||
} else {
|
||||
let (code, formattedNumber) = self.controllerNode.formattedCodeAndNumber
|
||||
if let validLayout = self.validLayout, validLayout.size.width > 320.0 {
|
||||
let (code, formattedNumber) = self.controllerNode.formattedCodeAndNumber
|
||||
|
||||
let confirmationController = PhoneConfirmationController(theme: self.presentationData.theme, strings: self.presentationData.strings, code: code, number: formattedNumber, sourceController: self)
|
||||
confirmationController.proceed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
||||
let confirmationController = PhoneConfirmationController(theme: self.presentationData.theme, strings: self.presentationData.strings, code: code, number: formattedNumber, sourceController: self)
|
||||
confirmationController.proceed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
||||
}
|
||||
}
|
||||
(self.navigationController as? NavigationController)?.presentOverlay(controller: confirmationController, inGlobal: true, blockInteraction: true)
|
||||
self.confirmationController = confirmationController
|
||||
} else {
|
||||
var actions: [TextAlertAction] = []
|
||||
actions.append(TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_Edit, action: {}))
|
||||
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Login_Yes, action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
||||
}
|
||||
}))
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root))
|
||||
}
|
||||
(self.navigationController as? NavigationController)?.presentOverlay(controller: confirmationController, inGlobal: true, blockInteraction: true)
|
||||
self.confirmationController = confirmationController
|
||||
}
|
||||
} else {
|
||||
self.hapticFeedback.error()
|
||||
|
||||
+110
-57
@@ -227,7 +227,7 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
self.phoneBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - 57.0), size: CGSize(width: size.width - inset, height: 57.0))
|
||||
|
||||
let countryCodeFrame = CGRect(origin: CGPoint(x: 18.0, y: size.height - 58.0), size: CGSize(width: 71.0, height: 57.0))
|
||||
let numberFrame = CGRect(origin: CGPoint(x: 107.0, y: size.height - 58.0), size: CGSize(width: size.width - 96.0 - 8.0, height: 57.0))
|
||||
let numberFrame = CGRect(origin: CGPoint(x: 107.0, y: size.height - 58.0), size: CGSize(width: size.width - 96.0 - 8.0 - 24.0, height: 57.0))
|
||||
let placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 17.0 - UIScreenPixel)
|
||||
|
||||
let phoneInputFrame = countryCodeFrame.union(numberFrame)
|
||||
@@ -360,7 +360,6 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroPhone"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
self.managedAnimationNode = ManagedPhoneAnimationNode()
|
||||
self.managedAnimationNode.isHidden = true
|
||||
@@ -479,9 +478,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
func animateIn(buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView) {
|
||||
self.proceedNode.animateTitle(to: self.strings.Login_Continue)
|
||||
|
||||
let duration: Double = 0.3
|
||||
|
||||
|
||||
self.animationSnapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.animationSnapshotView?.removeFromSuperview()
|
||||
self?.animationSnapshotView = nil
|
||||
@@ -507,7 +504,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
for node in nodes {
|
||||
node.alpha = 1.0
|
||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,11 +515,13 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [])
|
||||
insets.top = layout.statusBarHeight ?? 20.0
|
||||
|
||||
if let inputHeight = layout.inputHeight, !inputHeight.isZero {
|
||||
insets.bottom = max(inputHeight, insets.bottom)
|
||||
}
|
||||
|
||||
let titleInset: CGFloat = layout.size.width > 320.0 ? 18.0 : 0.0
|
||||
let additionalBottomInset: CGFloat = layout.size.width > 320.0 ? 80.0 : 10.0
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
|
||||
let inset: CGFloat = 24.0
|
||||
@@ -534,11 +533,23 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
let proceedSize = CGSize(width: layout.size.width - inset * 2.0, height: proceedHeight)
|
||||
|
||||
var items: [AuthorizationLayoutItem] = [
|
||||
AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: titleInset, maxValue: titleInset), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
]
|
||||
|
||||
if layout.size.width > 320.0 {
|
||||
items.insert(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), at: 0)
|
||||
self.proceedNode.isHidden = false
|
||||
self.animationNode.isHidden = false
|
||||
self.animationNode.visibility = true
|
||||
} else {
|
||||
insets.top = navigationBarHeight
|
||||
self.proceedNode.isHidden = true
|
||||
self.animationNode.isHidden = true
|
||||
self.managedAnimationNode.isHidden = true
|
||||
}
|
||||
|
||||
let contactSyncSize = self.contactSyncNode.updateLayout(width: layout.size.width)
|
||||
if self.hasOtherAccounts {
|
||||
self.contactSyncNode.isHidden = false
|
||||
@@ -558,7 +569,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
|
||||
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 - 80.0)), items: items, transition: transition, failIfDoesNotFit: false)
|
||||
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 - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false)
|
||||
|
||||
transition.updateFrame(node: self.managedAnimationNode, frame: self.animationNode.frame)
|
||||
}
|
||||
@@ -696,7 +707,12 @@ final class PhoneConfirmationController: ViewController {
|
||||
|
||||
var proceed: () -> Void = {}
|
||||
|
||||
class Node: ASDisplayNode {
|
||||
class Node: ASDisplayNode {
|
||||
private let theme: PresentationTheme
|
||||
|
||||
private let code: String
|
||||
private let number: String
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
@@ -714,7 +730,14 @@ final class PhoneConfirmationController: ViewController {
|
||||
var proceed: () -> Void = {}
|
||||
var cancel: () -> Void = {}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String) {
|
||||
self.theme = theme
|
||||
|
||||
self.code = code
|
||||
self.number = number
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||
|
||||
@@ -753,7 +776,6 @@ final class PhoneConfirmationController: ViewController {
|
||||
self.codeTargetNode.displaysAsynchronously = false
|
||||
self.codeTargetNode.attributedText = NSAttributedString(string: code, font: largeFont, textColor: theme.list.itemPrimaryTextColor)
|
||||
|
||||
|
||||
self.phoneTargetNode = ImmediateTextNode()
|
||||
self.phoneTargetNode.displaysAsynchronously = false
|
||||
|
||||
@@ -801,6 +823,9 @@ final class PhoneConfirmationController: ViewController {
|
||||
}
|
||||
|
||||
func animateIn(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let codeFrame = codeNode.convert(codeNode.bounds, to: nil)
|
||||
let numberFrame = numberNode.convert(numberNode.bounds, to: nil)
|
||||
let buttonFrame = buttonNode.convert(buttonNode.bounds, to: nil)
|
||||
@@ -811,43 +836,45 @@ final class PhoneConfirmationController: ViewController {
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
|
||||
let codeSize = self.codeSourceNode.updateLayout(self.frame.size)
|
||||
let duration: Double = 0.25
|
||||
|
||||
let codeSize = self.codeSourceNode.updateLayout(layout.size)
|
||||
self.codeSourceNode.frame = CGRect(origin: CGPoint(x: codeFrame.midX - codeSize.width / 2.0, y: codeFrame.midY - codeSize.height / 2.0), size: codeSize)
|
||||
|
||||
let numberSize = self.phoneSourceNode.updateLayout(self.frame.size)
|
||||
let numberSize = self.phoneSourceNode.updateLayout(layout.size)
|
||||
self.phoneSourceNode.frame = CGRect(origin: CGPoint(x: numberFrame.minX, y: numberFrame.midY - numberSize.height / 2.0), size: numberSize)
|
||||
|
||||
let targetScale = codeSize.height / self.codeTargetNode.frame.height
|
||||
let sourceScale = self.codeTargetNode.frame.height / codeSize.height
|
||||
|
||||
self.codeSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: 0.3)
|
||||
self.codeSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
self.codeSourceNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: 0.3)
|
||||
self.codeSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: duration)
|
||||
self.codeSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
|
||||
self.codeSourceNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: duration)
|
||||
|
||||
self.phoneSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: duration)
|
||||
self.phoneSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
|
||||
self.phoneSourceNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: duration)
|
||||
|
||||
self.codeTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: 0.3)
|
||||
self.codeTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.codeTargetNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: 0.3)
|
||||
self.codeTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: duration)
|
||||
self.codeTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.codeTargetNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: duration)
|
||||
|
||||
self.phoneTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: 0.3)
|
||||
self.phoneTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.phoneTargetNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: 0.3)
|
||||
self.phoneTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: duration)
|
||||
self.phoneTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.phoneTargetNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: duration)
|
||||
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
self.backgroundNode.layer.animateFrame(from: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), to: self.backgroundNode.frame, duration: 0.3)
|
||||
self.backgroundNode.layer.animateFrame(from: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), to: self.backgroundNode.frame, duration: duration)
|
||||
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.textNode.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3)
|
||||
self.textNode.layer.animatePosition(from: CGPoint(x: -100.0, y: -45.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.textNode.layer.animateScale(from: 0.5, to: 1.0, duration: duration)
|
||||
self.textNode.layer.animatePosition(from: CGPoint(x: -100.0, y: -45.0), to: CGPoint(), duration: duration, additive: true)
|
||||
|
||||
self.cancelButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.cancelButton.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3)
|
||||
self.cancelButton.layer.animatePosition(from: CGPoint(x: -100.0, y: -70.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||
self.cancelButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.cancelButton.layer.animateScale(from: 0.5, to: 1.0, duration: duration)
|
||||
self.cancelButton.layer.animatePosition(from: CGPoint(x: -100.0, y: -70.0), to: CGPoint(), duration: duration, additive: true)
|
||||
|
||||
self.proceedNode.layer.animatePosition(from: buttonFrame.center, to: self.proceedNode.position, duration: 0.3)
|
||||
self.proceedNode.layer.animatePosition(from: buttonFrame.center, to: self.proceedNode.position, duration: duration)
|
||||
}
|
||||
|
||||
func animateOut(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode, completion: @escaping () -> Void) {
|
||||
@@ -857,6 +884,8 @@ final class PhoneConfirmationController: ViewController {
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
let duration: Double = 0.25
|
||||
|
||||
let codeSize = self.codeSourceNode.updateLayout(self.frame.size)
|
||||
self.codeSourceNode.frame = CGRect(origin: CGPoint(x: codeFrame.midX - codeSize.width / 2.0, y: codeFrame.midY - codeSize.height / 2.0), size: codeSize)
|
||||
|
||||
@@ -866,45 +895,48 @@ final class PhoneConfirmationController: ViewController {
|
||||
let targetScale = codeSize.height / self.codeTargetNode.frame.height
|
||||
let sourceScale = self.codeTargetNode.frame.height / codeSize.height
|
||||
|
||||
self.codeSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3)
|
||||
self.codeSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.codeSourceNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: 0.3)
|
||||
self.codeSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: duration)
|
||||
self.codeSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.codeSourceNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: duration)
|
||||
|
||||
self.phoneSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: 0.3)
|
||||
self.phoneSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: duration)
|
||||
self.phoneSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
self.phoneSourceNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: duration)
|
||||
|
||||
self.codeTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3)
|
||||
self.codeTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
self.codeTargetNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: 0.3)
|
||||
self.codeTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: duration)
|
||||
self.codeTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.codeTargetNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: duration)
|
||||
|
||||
Queue.mainQueue().after(0.25) {
|
||||
Queue.mainQueue().after(0.23) {
|
||||
codeNode.isHidden = false
|
||||
numberNode.isHidden = false
|
||||
buttonNode.isHidden = false
|
||||
}
|
||||
|
||||
self.phoneTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3)
|
||||
self.phoneTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
self.phoneTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: duration)
|
||||
self.phoneTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.phoneTargetNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: 0.3)
|
||||
self.phoneTargetNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: duration)
|
||||
|
||||
self.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.1, removeOnCompletion: false)
|
||||
self.backgroundNode.layer.animateFrame(from: self.backgroundNode.frame, to: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), duration: 0.3)
|
||||
self.backgroundNode.layer.animateFrame(from: self.backgroundNode.frame, to: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), duration: duration)
|
||||
|
||||
self.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.textNode.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, removeOnCompletion: false)
|
||||
self.textNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -45.0), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||
self.textNode.layer.animateScale(from: 1.0, to: 0.5, duration: duration, removeOnCompletion: false)
|
||||
self.textNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -45.0), duration: duration, removeOnCompletion: false, additive: true)
|
||||
|
||||
self.cancelButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.cancelButton.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, removeOnCompletion: false)
|
||||
self.cancelButton.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -70.0), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||
self.cancelButton.layer.animateScale(from: 1.0, to: 0.5, duration: duration, removeOnCompletion: false)
|
||||
self.cancelButton.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -70.0), duration: duration, removeOnCompletion: false, additive: true)
|
||||
|
||||
self.proceedNode.layer.animatePosition(from: self.proceedNode.position, to: buttonFrame.center, duration: 0.3, removeOnCompletion: false)
|
||||
self.proceedNode.layer.animatePosition(from: self.proceedNode.position, to: buttonFrame.center, duration: duration, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
let hadLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
let sideInset: CGFloat = 8.0
|
||||
let innerInset: CGFloat = 18.0
|
||||
|
||||
@@ -913,16 +945,33 @@ final class PhoneConfirmationController: ViewController {
|
||||
let backgroundSize = CGSize(width: layout.size.width - sideInset * 2.0, height: 243.0)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - backgroundSize.width) / 2.0), y: layout.size.height - backgroundSize.height - 260.0), size: backgroundSize)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||
|
||||
let maxWidth = layout.size.width - 20.0
|
||||
if !hadLayout {
|
||||
var fontSize = 34.0
|
||||
if layout.size.width < 375.0 {
|
||||
fontSize = 30.0
|
||||
}
|
||||
|
||||
let largeFont = Font.with(size: fontSize, design: .regular, weight: .bold, traits: [.monospacedNumbers])
|
||||
|
||||
self.codeTargetNode.attributedText = NSAttributedString(string: self.code, font: largeFont, textColor: self.theme.list.itemPrimaryTextColor)
|
||||
let targetString = NSMutableAttributedString(string: self.number, font: largeFont, textColor: self.theme.list.itemPrimaryTextColor)
|
||||
targetString.addAttribute(NSAttributedString.Key.kern, value: 1.6, range: NSRange(location: 0, length: targetString.length))
|
||||
self.phoneTargetNode.attributedText = targetString
|
||||
}
|
||||
|
||||
let codeSize = self.codeTargetNode.updateLayout(backgroundSize)
|
||||
let numberSize = self.phoneTargetNode.updateLayout(backgroundSize)
|
||||
let spacing: CGFloat = 10.0
|
||||
|
||||
let totalWidth = codeSize.width + numberSize.width + 10.0
|
||||
let codeSize = self.codeTargetNode.updateLayout(CGSize(width: maxWidth, height: .greatestFiniteMagnitude))
|
||||
let numberSize = self.phoneTargetNode.updateLayout(CGSize(width: maxWidth - codeSize.width - spacing, height: .greatestFiniteMagnitude))
|
||||
|
||||
let totalWidth = codeSize.width + numberSize.width + spacing
|
||||
|
||||
let codeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - totalWidth) / 2.0), y: 30.0), size: codeSize)
|
||||
transition.updateFrame(node: self.codeTargetNode, frame: codeFrame.offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||
|
||||
let numberFrame = CGRect(origin: CGPoint(x: codeFrame.maxX + 10.0, y: 30.0), size: numberSize)
|
||||
let numberFrame = CGRect(origin: CGPoint(x: codeFrame.maxX + spacing, y: 30.0), size: numberSize)
|
||||
transition.updateFrame(node: self.phoneTargetNode, frame: numberFrame.offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||
|
||||
let textSize = self.textNode.updateLayout(backgroundSize)
|
||||
@@ -972,6 +1021,10 @@ final class PhoneConfirmationController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func dismissAnimated() {
|
||||
self.controllerNode.cancel()
|
||||
}
|
||||
|
||||
func transitionOut() {
|
||||
self.controllerNode.cancel()
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
return self.displayNode as! AuthorizationSequenceSignUpControllerNode
|
||||
}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let presentationData: PresentationData
|
||||
private let back: () -> Void
|
||||
|
||||
@@ -30,12 +32,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
|
||||
var inProgress: Bool = false {
|
||||
didSet {
|
||||
if self.inProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
self.updateNavigationItems()
|
||||
self.controllerNode.inProgress = self.inProgress
|
||||
}
|
||||
}
|
||||
@@ -81,6 +78,19 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
|
||||
func updateNavigationItems() {
|
||||
guard let layout = self.validLayout, layout.size.width < 360.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||
}
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
let currentAvatarMixin = Atomic<NSObject?>(value: nil)
|
||||
|
||||
@@ -138,6 +148,13 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let hadLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
if !hadLayout {
|
||||
self.updateNavigationItems()
|
||||
}
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
|
||||
+23
-76
@@ -67,6 +67,14 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
|
||||
didSet {
|
||||
self.firstNameField.alpha = self.inProgress ? 0.6 : 1.0
|
||||
self.lastNameField.alpha = self.inProgress ? 0.6 : 1.0
|
||||
|
||||
if self.inProgress != oldValue {
|
||||
if self.inProgress {
|
||||
self.proceedNode.transitionToProgress()
|
||||
} else {
|
||||
self.proceedNode.transitionFromProgress()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,88 +214,19 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
|
||||
if let inputHeight = layout.inputHeight {
|
||||
insets.bottom += max(inputHeight, layout.standardInputHeight)
|
||||
}
|
||||
|
||||
let additionalBottomInset: CGFloat = layout.size.width > 320.0 ? 80.0 : 10.0
|
||||
|
||||
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 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 avatarSize: CGSize = CGSize(width: 110.0, height: 110.0)
|
||||
var items: [AuthorizationLayoutItem] = []
|
||||
@@ -305,11 +244,19 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
|
||||
|
||||
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 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)
|
||||
if layout.size.width > 320.0 {
|
||||
self.proceedNode.isHidden = false
|
||||
|
||||
let inset: CGFloat = 24.0
|
||||
let proceedHeight = self.proceedNode.updateLayout(width: layout.size.width - 48.0, transition: transition)
|
||||
let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight)
|
||||
transition.updateFrame(node: self.proceedNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - proceedSize.width) / 2.0), y: layout.size.height - insets.bottom - proceedSize.height - inset), size: proceedSize))
|
||||
} else {
|
||||
insets.top = navigationBarHeight
|
||||
self.proceedNode.isHidden = true
|
||||
}
|
||||
|
||||
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 - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false)
|
||||
}
|
||||
|
||||
func activateInput() {
|
||||
|
||||
@@ -2136,21 +2136,21 @@ open class TextView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
if !line.strikethroughs.isEmpty {
|
||||
for strikethrough in line.strikethroughs {
|
||||
var textColor: UIColor?
|
||||
layout.attributedString?.enumerateAttributes(in: NSMakeRange(line.range.location, line.range.length), options: []) { attributes, range, _ in
|
||||
if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor {
|
||||
textColor = color
|
||||
}
|
||||
}
|
||||
if let textColor = textColor {
|
||||
context.setFillColor(textColor.cgColor)
|
||||
}
|
||||
let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
|
||||
context.fill(CGRect(x: frame.minX, y: frame.minY - 5.0, width: frame.width, height: 1.0))
|
||||
}
|
||||
}
|
||||
// if !line.strikethroughs.isEmpty {
|
||||
// for strikethrough in line.strikethroughs {
|
||||
// var textColor: UIColor?
|
||||
// layout.attributedString?.enumerateAttributes(in: NSMakeRange(line.range.location, line.range.length), options: []) { attributes, range, _ in
|
||||
// if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor {
|
||||
// textColor = color
|
||||
// }
|
||||
// }
|
||||
// if let textColor = textColor {
|
||||
// context.setFillColor(textColor.cgColor)
|
||||
// }
|
||||
// let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
|
||||
// context.fill(CGRect(x: frame.minX, y: frame.minY - 5.0, width: frame.width, height: 1.0))
|
||||
// }
|
||||
// }
|
||||
|
||||
if !line.spoilers.isEmpty {
|
||||
if layout.displaySpoilers {
|
||||
|
||||
@@ -10,6 +10,7 @@ import PersistentStringHash
|
||||
|
||||
private let productIdentifiers = [
|
||||
"org.telegram.telegramPremium.annual",
|
||||
"org.telegram.telegramPremium.semiannual",
|
||||
"org.telegram.telegramPremium.monthly",
|
||||
"org.telegram.telegramPremium.twelveMonths",
|
||||
"org.telegram.telegramPremium.sixMonths",
|
||||
@@ -17,7 +18,7 @@ private let productIdentifiers = [
|
||||
]
|
||||
|
||||
private func isSubscriptionProductId(_ id: String) -> Bool {
|
||||
return id.hasSuffix(".monthly") || id.hasSuffix(".annual")
|
||||
return id.hasSuffix(".monthly") || id.hasSuffix(".annual") || id.hasSuffix(".semiannual")
|
||||
}
|
||||
|
||||
private extension NSDecimalNumber {
|
||||
@@ -30,6 +31,22 @@ private extension NSDecimalNumber {
|
||||
raiseOnUnderflow: false,
|
||||
raiseOnDivideByZero: false))
|
||||
}
|
||||
|
||||
func prettyPrice() -> NSDecimalNumber {
|
||||
return self.multiplying(by: NSDecimalNumber(value: 2))
|
||||
.rounding(accordingToBehavior:
|
||||
NSDecimalNumberHandler(
|
||||
roundingMode: .plain,
|
||||
scale: Int16(0),
|
||||
raiseOnExactness: false,
|
||||
raiseOnOverflow: false,
|
||||
raiseOnUnderflow: false,
|
||||
raiseOnDivideByZero: false
|
||||
)
|
||||
)
|
||||
.dividing(by: NSDecimalNumber(value: 2))
|
||||
.subtracting(NSDecimalNumber(value: 0.01))
|
||||
}
|
||||
}
|
||||
|
||||
public final class InAppPurchaseManager: NSObject {
|
||||
@@ -57,7 +74,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
} else if #available(iOS 11.2, *) {
|
||||
return self.skProduct.subscriptionPeriod != nil
|
||||
} else {
|
||||
return self.id.hasSuffix(".monthly") || self.id.hasSuffix(".annual")
|
||||
return self.id.hasSuffix(".monthly") || self.id.hasSuffix(".annual") || self.id.hasSuffix(".semiannual")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +83,27 @@ public final class InAppPurchaseManager: NSObject {
|
||||
}
|
||||
|
||||
public func pricePerMonth(_ monthsCount: Int) -> String {
|
||||
let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).round(2)
|
||||
let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).prettyPrice().round(2)
|
||||
return self.numberFormatter.string(from: price) ?? ""
|
||||
}
|
||||
|
||||
public func defaultPrice(_ value: NSDecimalNumber, monthsCount: Int) -> String {
|
||||
let price = value.multiplying(by: NSDecimalNumber(value: monthsCount)).round(2)
|
||||
return self.numberFormatter.string(from: price) ?? ""
|
||||
let prettierPrice = price
|
||||
.multiplying(by: NSDecimalNumber(value: 2))
|
||||
.rounding(accordingToBehavior:
|
||||
NSDecimalNumberHandler(
|
||||
roundingMode: .up,
|
||||
scale: Int16(0),
|
||||
raiseOnExactness: false,
|
||||
raiseOnOverflow: false,
|
||||
raiseOnUnderflow: false,
|
||||
raiseOnDivideByZero: false
|
||||
)
|
||||
)
|
||||
.dividing(by: NSDecimalNumber(value: 2))
|
||||
.subtracting(NSDecimalNumber(value: 0.01))
|
||||
return self.numberFormatter.string(from: prettierPrice) ?? ""
|
||||
}
|
||||
|
||||
public var priceValue: NSDecimalNumber {
|
||||
|
||||
@@ -75,9 +75,10 @@ class EmojiHeaderComponent: Component {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.statusView = ComponentHostView<Empty>()
|
||||
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
|
||||
self.statusView.isHidden = true
|
||||
self.addSubview(self.statusView)
|
||||
|
||||
self.disablesInteractiveModalDismiss = true
|
||||
@@ -87,13 +88,29 @@ class EmojiHeaderComponent: Component {
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
var scheduledAnimateIn = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
|
||||
if self.scheduledAnimateIn {
|
||||
self.animateIn()
|
||||
self.scheduledAnimateIn = false
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
guard let animateFrom = self.animateFrom, var containerView = self.containerView else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let _ = self.window else {
|
||||
self.scheduledAnimateIn = true
|
||||
return
|
||||
}
|
||||
|
||||
containerView = containerView.subviews[2].subviews[1]
|
||||
self.statusView.isHidden = false
|
||||
containerView = containerView.subviews[1].subviews[1]
|
||||
|
||||
let initialPosition = self.statusView.center
|
||||
let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView)
|
||||
|
||||
@@ -548,7 +548,6 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
).start(next: { [weak self] products, peer in
|
||||
if let strongSelf = self {
|
||||
|
||||
var gifts: [PremiumGiftProduct] = []
|
||||
for option in strongSelf.options {
|
||||
if let product = products.first(where: { $0.id == option.storeProductId }), !product.isSubscription {
|
||||
|
||||
@@ -305,6 +305,27 @@ struct PremiumIntroConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private struct PremiumProduct: Equatable {
|
||||
let option: PremiumPromoConfiguration.PremiumProductOption
|
||||
let storeProduct: InAppPurchaseManager.Product
|
||||
|
||||
var id: String {
|
||||
return self.storeProduct.id
|
||||
}
|
||||
|
||||
var months: Int32 {
|
||||
return self.option.months
|
||||
}
|
||||
|
||||
var price: String {
|
||||
return self.storeProduct.price
|
||||
}
|
||||
|
||||
var pricePerMonth: String {
|
||||
return self.storeProduct.pricePerMonth(Int(self.months))
|
||||
}
|
||||
}
|
||||
|
||||
final class PremiumOptionComponent: CombinedComponent {
|
||||
let title: String
|
||||
let subtitle: String
|
||||
@@ -419,7 +440,7 @@ final class PremiumOptionComponent: CombinedComponent {
|
||||
)
|
||||
|
||||
var spacing: CGFloat = 0.0
|
||||
var subtitleHeight: CGFloat = 0.0
|
||||
var subtitleSize = CGSize()
|
||||
if !component.subtitle.isEmpty {
|
||||
spacing = 2.0
|
||||
|
||||
@@ -447,7 +468,7 @@ final class PremiumOptionComponent: CombinedComponent {
|
||||
context.add(subtitle
|
||||
.position(CGPoint(x: insets.left + subtitle.size.width / 2.0, y: insets.top + title.size.height + spacing + subtitle.size.height / 2.0))
|
||||
)
|
||||
subtitleHeight = subtitle.size.height
|
||||
subtitleSize = subtitle.size
|
||||
|
||||
insets.top -= 2.0
|
||||
insets.bottom -= 2.0
|
||||
@@ -512,10 +533,19 @@ final class PremiumOptionComponent: CombinedComponent {
|
||||
.position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0))
|
||||
)
|
||||
|
||||
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitleHeight + insets.bottom)
|
||||
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitleSize.height + insets.bottom)
|
||||
|
||||
let distance = context.availableSize.width - insets.left - insets.right - label.size.width - subtitleSize.width
|
||||
|
||||
let labelY: CGFloat
|
||||
if distance > 8.0 {
|
||||
labelY = size.height / 2.0
|
||||
} else {
|
||||
labelY = insets.top + title.size.height / 2.0
|
||||
}
|
||||
|
||||
context.add(label
|
||||
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: size.height / 2.0))
|
||||
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: labelY))
|
||||
)
|
||||
|
||||
context.add(check
|
||||
@@ -970,20 +1000,22 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
let source: PremiumSource
|
||||
let isPremium: Bool?
|
||||
let otherPeerName: String?
|
||||
let products: [InAppPurchaseManager.Product]?
|
||||
let products: [PremiumProduct]?
|
||||
let selectedProductId: String?
|
||||
let promoConfiguration: PremiumPromoConfiguration?
|
||||
let present: (ViewController) -> Void
|
||||
let selectProduct: (String) -> Void
|
||||
let buy: () -> Void
|
||||
let updateIsFocused: (Bool) -> Void
|
||||
|
||||
init(context: AccountContext, source: PremiumSource, isPremium: Bool?, otherPeerName: String?, products: [InAppPurchaseManager.Product]?, selectedProductId: String?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void, buy: @escaping () -> Void, updateIsFocused: @escaping (Bool) -> Void) {
|
||||
init(context: AccountContext, source: PremiumSource, isPremium: Bool?, otherPeerName: String?, products: [PremiumProduct]?, selectedProductId: String?, promoConfiguration: PremiumPromoConfiguration?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void, buy: @escaping () -> Void, updateIsFocused: @escaping (Bool) -> Void) {
|
||||
self.context = context
|
||||
self.source = source
|
||||
self.isPremium = isPremium
|
||||
self.otherPeerName = otherPeerName
|
||||
self.products = products
|
||||
self.selectedProductId = selectedProductId
|
||||
self.promoConfiguration = promoConfiguration
|
||||
self.present = present
|
||||
self.selectProduct = selectProduct
|
||||
self.buy = buy
|
||||
@@ -1009,6 +1041,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
if lhs.selectedProductId != rhs.selectedProductId {
|
||||
return false
|
||||
}
|
||||
if lhs.promoConfiguration != rhs.promoConfiguration {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1016,15 +1051,14 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
|
||||
var products: [InAppPurchaseManager.Product]?
|
||||
var products: [PremiumProduct]?
|
||||
var selectedProductId: String?
|
||||
|
||||
var isPremium: Bool?
|
||||
|
||||
private var disposable: Disposable?
|
||||
private(set) var configuration = PremiumIntroConfiguration.defaultValue
|
||||
private(set) var promoConfiguration: PremiumPromoConfiguration?
|
||||
|
||||
|
||||
private var stickersDisposable: Disposable?
|
||||
private var preloadDisposableSet = DisposableSet()
|
||||
|
||||
@@ -1042,13 +1076,11 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
super.init()
|
||||
|
||||
self.disposable = (context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Configuration.App(),
|
||||
TelegramEngine.EngineData.Item.Configuration.PremiumPromo()
|
||||
TelegramEngine.EngineData.Item.Configuration.App()
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration, promoConfiguration in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration in
|
||||
if let strongSelf = self {
|
||||
strongSelf.configuration = PremiumIntroConfiguration.with(appConfiguration: appConfiguration)
|
||||
strongSelf.promoConfiguration = promoConfiguration
|
||||
strongSelf.updated(transition: .immediate)
|
||||
|
||||
if let identifier = source.identifier {
|
||||
@@ -1070,10 +1102,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_show", data: json)
|
||||
}
|
||||
}
|
||||
|
||||
for (_, video) in promoConfiguration.videos {
|
||||
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1253,7 +1281,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
|
||||
let shortestOptionPrice: (Int64, NSDecimalNumber)
|
||||
if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) {
|
||||
shortestOptionPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue)
|
||||
shortestOptionPrice = (Int64(Float(product.storeProduct.priceCurrencyAndAmount.amount)), product.storeProduct.priceValue)
|
||||
} else {
|
||||
shortestOptionPrice = (1, NSDecimalNumber(decimal: 1))
|
||||
}
|
||||
@@ -1261,16 +1289,17 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
var i = 0
|
||||
for product in products {
|
||||
let giftTitle: String
|
||||
let months: Float
|
||||
let months = product.months
|
||||
|
||||
if product.id.hasSuffix(".monthly") {
|
||||
giftTitle = strings.Premium_Monthly
|
||||
months = 1
|
||||
} else if product.id.hasSuffix(".semiannual") {
|
||||
giftTitle = strings.Premium_Semiannual
|
||||
} else {
|
||||
giftTitle = strings.Premium_Annual
|
||||
months = 12
|
||||
}
|
||||
|
||||
let discountValue = Int((1.0 - Float(product.priceCurrencyAndAmount.amount) / months / Float(shortestOptionPrice.0)) * 100.0)
|
||||
let discountValue = Int((1.0 - Float(product.storeProduct.priceCurrencyAndAmount.amount) / Float(months) / Float(shortestOptionPrice.0)) * 100.0)
|
||||
let discount: String
|
||||
if discountValue > 0 {
|
||||
discount = "-\(discountValue)%"
|
||||
@@ -1278,12 +1307,12 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
discount = ""
|
||||
}
|
||||
|
||||
let defaultPrice = product.defaultPrice(shortestOptionPrice.1, monthsCount: Int(months))
|
||||
let defaultPrice = product.storeProduct.defaultPrice(shortestOptionPrice.1, monthsCount: Int(months))
|
||||
|
||||
var subtitle = ""
|
||||
var pricePerMonth = product.price
|
||||
if months > 1 {
|
||||
pricePerMonth = product.pricePerMonth(Int(months))
|
||||
pricePerMonth = product.storeProduct.pricePerMonth(Int(months))
|
||||
|
||||
if discountValue > 0 {
|
||||
subtitle = "**\(defaultPrice)** \(product.price)"
|
||||
@@ -1552,7 +1581,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
let termsString: MultilineTextComponent.TextContent
|
||||
if isGiftView {
|
||||
termsString = .plain(NSAttributedString())
|
||||
} else if let promoConfiguration = context.state.promoConfiguration {
|
||||
} else if let promoConfiguration = context.component.promoConfiguration {
|
||||
let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont, message: nil)
|
||||
termsString = .plain(attributedString)
|
||||
} else {
|
||||
@@ -1723,8 +1752,10 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
|
||||
var inProgress = false
|
||||
|
||||
var products: [InAppPurchaseManager.Product]?
|
||||
var selectedProductId: String?
|
||||
private(set) var promoConfiguration: PremiumPromoConfiguration?
|
||||
|
||||
private(set) var products: [PremiumProduct]?
|
||||
private(set) var selectedProductId: String?
|
||||
|
||||
var isPremium: Bool?
|
||||
var otherPeerName: String?
|
||||
@@ -1740,6 +1771,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
private var disposable: Disposable?
|
||||
private var paymentDisposable = MetaDisposable()
|
||||
private var activationDisposable = MetaDisposable()
|
||||
private var preloadDisposableSet = DisposableSet()
|
||||
|
||||
var price: String? {
|
||||
return self.products?.first(where: { $0.id == self.selectedProductId })?.price
|
||||
@@ -1792,20 +1824,37 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
self.disposable = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
availableProducts,
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.PremiumPromo()),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> map { peer -> Bool in
|
||||
return peer?.isPremium ?? false
|
||||
},
|
||||
otherPeerName
|
||||
).start(next: { [weak self] products, isPremium, otherPeerName in
|
||||
).start(next: { [weak self] availableProducts, promoConfiguration, isPremium, otherPeerName in
|
||||
if let strongSelf = self {
|
||||
strongSelf.promoConfiguration = promoConfiguration
|
||||
|
||||
let hadProducts = strongSelf.products != nil
|
||||
strongSelf.products = products.filter { $0.isSubscription }
|
||||
if !hadProducts {
|
||||
strongSelf.selectedProductId = strongSelf.products?.last?.id
|
||||
|
||||
var products: [PremiumProduct] = []
|
||||
for option in promoConfiguration.premiumProductOptions {
|
||||
if let product = availableProducts.first(where: { $0.id == option.storeProductId }), product.isSubscription {
|
||||
products.append(PremiumProduct(option: option, storeProduct: product))
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.products = products
|
||||
strongSelf.isPremium = isPremium
|
||||
strongSelf.otherPeerName = otherPeerName
|
||||
|
||||
if !hadProducts {
|
||||
strongSelf.selectedProductId = strongSelf.products?.last?.id
|
||||
|
||||
for (_, video) in promoConfiguration.videos {
|
||||
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
@@ -1833,6 +1882,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
self.paymentDisposable.dispose()
|
||||
self.activationDisposable.dispose()
|
||||
self.emojiFileDisposable?.dispose()
|
||||
self.preloadDisposableSet.dispose()
|
||||
}
|
||||
|
||||
func buy() {
|
||||
@@ -1851,7 +1901,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
||||
if let strongSelf = self {
|
||||
if available {
|
||||
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct)
|
||||
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct.storeProduct)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self, case .purchased = status {
|
||||
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
|
||||
@@ -2022,7 +2072,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
)
|
||||
|
||||
let titleString: String
|
||||
if case .giftTerms = context.component.source {
|
||||
if case .emojiStatus = context.component.source {
|
||||
titleString = environment.strings.Premium_Title
|
||||
} else if case .giftTerms = context.component.source {
|
||||
titleString = environment.strings.Premium_Title
|
||||
} else if case .gift = context.component.source {
|
||||
titleString = environment.strings.Premium_GiftedTitle
|
||||
@@ -2154,6 +2206,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
otherPeerName: state.otherPeerName,
|
||||
products: state.products,
|
||||
selectedProductId: state.selectedProductId,
|
||||
promoConfiguration: state.promoConfiguration,
|
||||
present: context.component.present,
|
||||
selectProduct: { [weak state] productId in
|
||||
state?.selectProduct(productId)
|
||||
@@ -2454,9 +2507,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
view.animateFrom = sourceView
|
||||
view.containerView = self.containerView
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
view.animateIn()
|
||||
}
|
||||
view.animateIn()
|
||||
|
||||
self.sourceView = nil
|
||||
self.containerView = nil
|
||||
|
||||
+86
-35
@@ -539,8 +539,18 @@ private func privacyAndSecurityControllerEntries(
|
||||
return entries
|
||||
}
|
||||
|
||||
class PrivacyAndSecurityControllerImpl: ItemListController {
|
||||
|
||||
class PrivacyAndSecurityControllerImpl: ItemListController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
|
||||
var authorizationCompletion: ((Any) -> Void)?
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
|
||||
self.authorizationCompletion?(authorization.credential)
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
|
||||
return self.view.window!
|
||||
}
|
||||
}
|
||||
|
||||
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil, loginEmailPattern: Signal<String?, NoError>? = nil) -> ViewController {
|
||||
@@ -1068,6 +1078,28 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
||||
updateHasTwoStepAuth()
|
||||
}
|
||||
|
||||
let emailChangeCompletion: (AuthorizationSequenceCodeEntryController?) -> Void = { codeController in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
codeController?.animateSuccess()
|
||||
Queue.mainQueue().after(0.75) {
|
||||
if let navigationController = getNavigationControllerImpl?() {
|
||||
let controllers = navigationController.viewControllers.filter { controller in
|
||||
if controller is AuthorizationSequenceEmailEntryController || controller is AuthorizationSequenceCodeEntryController {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|
||||
navigationController.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .emoji(name: "IntroLetter", text: presentationData.strings.Login_EmailChanged), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
|
||||
return false
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupEmailImpl = { emailPattern in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var dismissEmailControllerImpl: (() -> Void)?
|
||||
@@ -1083,6 +1115,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
||||
let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, openUrl: { _ in }, back: {
|
||||
dismissCodeControllerImpl?()
|
||||
})
|
||||
|
||||
codeController.loginWithCode = { [weak codeController] code in
|
||||
actionsDisposable.add((verifyLoginEmailChange(account: context.account, code: .emailCode(code))
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
@@ -1121,39 +1154,9 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
||||
}
|
||||
}
|
||||
}, completed: { [weak codeController] in
|
||||
codeController?.animateSuccess()
|
||||
Queue.mainQueue().after(0.75) {
|
||||
if let navigationController = getNavigationControllerImpl?() {
|
||||
let controllers = navigationController.viewControllers.filter { controller in
|
||||
if controller is AuthorizationSequenceEmailEntryController || controller is AuthorizationSequenceCodeEntryController {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|
||||
navigationController.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .emoji(name: "IntroLetter", text: presentationData.strings.Login_EmailChanged), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
|
||||
return false
|
||||
}))
|
||||
}
|
||||
}
|
||||
emailChangeCompletion(codeController)
|
||||
}))
|
||||
}
|
||||
codeController.signInWithApple = {
|
||||
// if #available(iOS 13.0, *) {
|
||||
// let appleIdProvider = ASAuthorizationAppleIDProvider()
|
||||
// let passwordProvider = ASAuthorizationPasswordProvider()
|
||||
// let request = appleIdProvider.createRequest()
|
||||
//
|
||||
// let passwordRequest = passwordProvider.createRequest()
|
||||
//
|
||||
// let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest])
|
||||
// authorizationController.delegate = strongSelf
|
||||
// authorizationController.presentationContextProvider = strongSelf
|
||||
// authorizationController.performRequests()
|
||||
// }
|
||||
}
|
||||
codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, nextPhoneLoginDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil)
|
||||
pushControllerImpl?(codeController, true)
|
||||
dismissCodeControllerImpl = { [weak codeController] in
|
||||
@@ -1181,8 +1184,56 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
||||
emailController?.inProgress = false
|
||||
}))
|
||||
}
|
||||
emailController.signInWithApple = {
|
||||
|
||||
emailController.signInWithApple = { [weak controller, weak emailController] in
|
||||
if #available(iOS 13.0, *) {
|
||||
let appleIdProvider = ASAuthorizationAppleIDProvider()
|
||||
let passwordProvider = ASAuthorizationPasswordProvider()
|
||||
let request = appleIdProvider.createRequest()
|
||||
|
||||
let passwordRequest = passwordProvider.createRequest()
|
||||
|
||||
let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest])
|
||||
authorizationController.delegate = controller
|
||||
authorizationController.presentationContextProvider = controller
|
||||
authorizationController.performRequests()
|
||||
|
||||
controller?.authorizationCompletion = { [weak controller, weak emailController] credential in
|
||||
guard let credential = credential as? ASAuthorizationCredential else {
|
||||
return
|
||||
}
|
||||
switch credential {
|
||||
case let appleIdCredential as ASAuthorizationAppleIDCredential:
|
||||
guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else {
|
||||
emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
return
|
||||
}
|
||||
actionsDisposable.add((verifyLoginEmailChange(account: context.account, code: .appleToken(token))
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = presentationData.strings.Login_CodeFloodError
|
||||
case .generic, .codeExpired:
|
||||
text = presentationData.strings.Login_UnknownError
|
||||
case .invalidCode:
|
||||
text = presentationData.strings.Login_InvalidCodeError
|
||||
case .timeout:
|
||||
text = presentationData.strings.Login_NetworkError
|
||||
case .invalidEmailToken:
|
||||
text = presentationData.strings.Login_InvalidEmailTokenError
|
||||
case .emailNotAllowed:
|
||||
text = presentationData.strings.Login_EmailNotAllowedError
|
||||
}
|
||||
emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, completed: { [weak controller] in
|
||||
controller?.authorizationCompletion = nil
|
||||
emailChangeCompletion(nil)
|
||||
}))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
emailController.updateData(appleSignInAllowed: true)
|
||||
pushControllerImpl?(emailController, true)
|
||||
|
||||
@@ -432,7 +432,7 @@ public func verifyLoginEmailChange(account: Account, code: AuthorizationCode.Ema
|
||||
}
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Never, AuthorizationEmailVerificationError> in
|
||||
return .never()
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2023,7 +2023,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
||||
|
||||
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, TelegramMediaFile?, String?, Bool) -> Void)?
|
||||
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, String)?, NoError>, Bool) -> Void)?
|
||||
|
||||
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
||||
|
||||
@@ -2034,8 +2034,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
var emojiStatusPackDisposable = MetaDisposable()
|
||||
var emojiStatusFile: TelegramMediaFile?
|
||||
var emojiStatusPackTitle: String?
|
||||
var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, String)?>()
|
||||
|
||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool) {
|
||||
self.context = context
|
||||
@@ -2214,7 +2213,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func invokeDisplayPremiumIntro() {
|
||||
self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, nil, nil, self.isAvatarExpanded)
|
||||
self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, .never(), self.isAvatarExpanded)
|
||||
}
|
||||
|
||||
func initiateAvatarExpansion(gallery: Bool, first: Bool) {
|
||||
@@ -2372,7 +2371,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFile, strongSelf.emojiStatusPackTitle, false)
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false)
|
||||
},
|
||||
longTapAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
@@ -2385,7 +2384,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let emojiFile = emojiFile {
|
||||
strongSelf.emojiStatusFile = nil
|
||||
strongSelf.emojiStatusFileAndPackTitle.set(.never())
|
||||
|
||||
for attribute in emojiFile.attributes {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
|
||||
strongSelf.emojiStatusPackDisposable.set((strongSelf.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false)
|
||||
@@ -2406,15 +2406,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.emojiStatusFile = fileAndPackTitle?.0
|
||||
strongSelf.emojiStatusPackTitle = fileAndPackTitle?.1
|
||||
strongSelf.emojiStatusFileAndPackTitle.set(.single(fileAndPackTitle))
|
||||
}))
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strongSelf.emojiStatusFile = nil
|
||||
strongSelf.emojiStatusPackDisposable.set(nil)
|
||||
strongSelf.emojiStatusFileAndPackTitle.set(.never())
|
||||
}
|
||||
}
|
||||
)),
|
||||
@@ -2433,7 +2431,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFile, strongSelf.emojiStatusPackTitle, true)
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true)
|
||||
},
|
||||
longTapAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
||||
@@ -3096,7 +3096,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}))
|
||||
}
|
||||
|
||||
self.headerNode.displayPremiumIntro = { [weak self] sourceView, _, _, _, _ in
|
||||
self.headerNode.displayPremiumIntro = { [weak self] sourceView, _, _, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -3131,7 +3131,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
} else {
|
||||
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext)
|
||||
|
||||
self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, emojiStatusFile, emojiPackTitle, white in
|
||||
self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, emojiStatusFileAndPackTitle, white in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -3146,18 +3146,31 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let source: PremiumSource
|
||||
if let peerStatus = peerStatus, let emojiStatusFile = emojiStatusFile {
|
||||
source = .emojiStatus(strongSelf.peerId, peerStatus.fileId, emojiStatusFile, emojiPackTitle)
|
||||
let source: Signal<PremiumSource, NoError>
|
||||
if let peerStatus = peerStatus {
|
||||
source = emojiStatusFileAndPackTitle
|
||||
|> take(1)
|
||||
|> mapToSignal { emojiStatusFileAndPackTitle -> Signal<PremiumSource, NoError> in
|
||||
if let (file, packTitle) = emojiStatusFileAndPackTitle {
|
||||
return .single(.emojiStatus(strongSelf.peerId, peerStatus.fileId, file, packTitle))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
source = .profile(strongSelf.peerId)
|
||||
source = .single(.profile(strongSelf.peerId))
|
||||
}
|
||||
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: source)
|
||||
controller.sourceView = sourceView
|
||||
controller.containerView = strongSelf.controller?.navigationController?.view
|
||||
controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor
|
||||
strongSelf.controller?.push(controller)
|
||||
let _ = source.start(next: { [weak self] source in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: source)
|
||||
controller.sourceView = sourceView
|
||||
controller.containerView = strongSelf.controller?.navigationController?.view
|
||||
controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor
|
||||
strongSelf.controller?.push(controller)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -802,6 +802,10 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
for basePath in baseTelegramMePaths {
|
||||
for scheme in schemes {
|
||||
let basePrefix = scheme + basePath + "/"
|
||||
var url = url
|
||||
if (url.lowercased().hasPrefix(scheme) && url.lowercased().hasSuffix(".\(basePath)")) {
|
||||
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)", with: "")
|
||||
}
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
||||
return resolveInternalUrl(context: context, url: internalUrl)
|
||||
|
||||
Reference in New Issue
Block a user