Various fixes

This commit is contained in:
Ilya Laktyushin 2022-08-29 05:25:29 +02:00
parent b4e057f444
commit e15dca9bcc
13 changed files with 438 additions and 183 deletions

View File

@ -7962,6 +7962,7 @@ Sorry for the inconvenience.";
"Emoji.FrequentlyUsed" = "Recently Used"; "Emoji.FrequentlyUsed" = "Recently Used";
"Premium.Annual" = "Annual"; "Premium.Annual" = "Annual";
"Premium.Semiannual" = "Semiannual";
"Premium.Monthly" = "Monthly"; "Premium.Monthly" = "Monthly";
"Login.PhoneNumberConfirmation" = "Is this the correct number?"; "Login.PhoneNumberConfirmation" = "Is this the correct number?";

View File

@ -11,6 +11,8 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode
} }
private var validLayout: ContainerViewLayout?
private let strings: PresentationStrings private let strings: PresentationStrings
private let theme: PresentationTheme private let theme: PresentationTheme
private let openUrl: (String) -> Void private let openUrl: (String) -> Void
@ -30,12 +32,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
public var inProgress: Bool = false { public var inProgress: Bool = false {
didSet { didSet {
// if self.inProgress { self.updateNavigationItems()
// 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.controllerNode.inProgress = self.inProgress self.controllerNode.inProgress = self.inProgress
} }
} }
@ -52,9 +49,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self.hasActiveInput = true self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style 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 self.attemptNavigation = { _ in
return false return false
} }
@ -124,6 +119,19 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self.controllerNode.animateError(text: text) 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)?) { public func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) {
self.termsOfService = termsOfService self.termsOfService = termsOfService
if self.data?.0 != number || self.data?.1 != email || self.data?.2 != codeType || self.data?.3 != nextType || self.data?.4 != timeout { 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) { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) 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) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
} }

View File

@ -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(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()
} }
})) }))
} }
@ -645,7 +647,6 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
// lastController?.inProgress = false
switch result { switch result {
case let .signUp(data): case let .signUp(data):
let _ = beginSignUp(account: strongSelf.account, data: data).start() let _ = beginSignUp(account: strongSelf.account, data: data).start()
@ -655,8 +656,6 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
}, error: { [weak self, weak lastController] error in }, error: { [weak self, weak lastController] error in
Queue.mainQueue().async { Queue.mainQueue().async {
if let strongSelf = self, let lastController = lastController { if let strongSelf = self, let lastController = lastController {
// controller.inProgress = false
let text: String let text: String
switch error { switch error {
case .limitExceeded: case .limitExceeded:

View File

@ -17,6 +17,8 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
return self.displayNode as! AuthorizationSequenceEmailEntryControllerNode return self.displayNode as! AuthorizationSequenceEmailEntryControllerNode
} }
private var validLayout: ContainerViewLayout?
private let presentationData: PresentationData private let presentationData: PresentationData
public var proceedWithEmail: ((String) -> Void)? public var proceedWithEmail: ((String) -> Void)?
@ -28,12 +30,7 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
public var inProgress: Bool = false { public var inProgress: Bool = false {
didSet { didSet {
// if self.inProgress { self.updateNavigationItems()
// 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.controllerNode.inProgress = self.inProgress self.controllerNode.inProgress = self.inProgress
} }
} }
@ -56,8 +53,6 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
self.navigationBar?.backPressed = { self.navigationBar?.backPressed = {
back() back()
} }
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
} }
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
@ -87,6 +82,19 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
self.controllerNode.activateInput() 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) { public func updateData(appleSignInAllowed: Bool) {
var appleSignInAllowed = appleSignInAllowed var appleSignInAllowed = appleSignInAllowed
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
@ -104,6 +112,13 @@ public final class AuthorizationSequenceEmailEntryController: ViewController {
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) 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) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
} }

View File

@ -87,7 +87,6 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
self.animationNode = DefaultAnimatedStickerNodeImpl() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) 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 = ASTextNode()
self.titleNode.isUserInteractionEnabled = false self.titleNode.isUserInteractionEnabled = false
@ -189,11 +188,12 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
var insets = layout.insets(options: []) var insets = layout.insets(options: [])
insets.top = layout.statusBarHeight ?? 20.0 insets.top = layout.statusBarHeight ?? 20.0
if let inputHeight = layout.inputHeight { if let inputHeight = layout.inputHeight {
insets.bottom = max(inputHeight, insets.bottom) 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) 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) let animationSize = CGSize(width: 100.0, height: 100.0)
@ -204,15 +204,25 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight) let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight)
var items: [AuthorizationLayoutItem] = [] 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.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.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))) 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 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) 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) transition.updateFrame(node: self.proceedNode, frame: buttonFrame)

View File

@ -17,6 +17,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
return self.displayNode as! AuthorizationSequencePhoneEntryControllerNode return self.displayNode as! AuthorizationSequencePhoneEntryControllerNode
} }
private var validLayout: ContainerViewLayout?
private let sharedContext: SharedAccountContext private let sharedContext: SharedAccountContext
private var account: UnauthorizedAccount private var account: UnauthorizedAccount
private let isTestingEnvironment: Bool private let isTestingEnvironment: Bool
@ -43,12 +45,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
var inProgress: Bool = false { var inProgress: Bool = false {
didSet { didSet {
// if self.inProgress { self.updateNavigationItems()
// 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.controllerNode.inProgress = self.inProgress self.controllerNode.inProgress = self.inProgress
self.confirmationController?.inProgress = self.inProgress self.confirmationController?.inProgress = self.inProgress
} }
@ -89,7 +86,6 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
if !otherAccountPhoneNumbers.1.isEmpty { if !otherAccountPhoneNumbers.1.isEmpty {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) 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) { required init(coder aDecoder: NSCoder) {
@ -104,6 +100,19 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.back() 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) { func updateData(countryCode: Int32, countryName: String?, number: String) {
self.currentData = (countryCode, countryName, number) self.currentData = (countryCode, countryName, number)
if self.isNodeLoaded { if self.isNodeLoaded {
@ -207,6 +216,13 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) 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) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
if self.shouldAnimateIn, let inputHeight = layout.inputHeight, inputHeight > 0.0 { 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() { @objc func nextPressed() {
let (_, _, number) = self.controllerNode.codeAndNumber let (_, _, number) = self.controllerNode.codeAndNumber
if !number.isEmpty { if !number.isEmpty {
@ -239,16 +260,27 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})) 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)) self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
} else { } 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) let confirmationController = PhoneConfirmationController(theme: self.presentationData.theme, strings: self.presentationData.strings, code: code, number: formattedNumber, sourceController: self)
confirmationController.proceed = { [weak self] in confirmationController.proceed = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts) 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 { } else {
self.hapticFeedback.error() self.hapticFeedback.error()

View File

@ -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)) 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 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 placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 17.0 - UIScreenPixel)
let phoneInputFrame = countryCodeFrame.union(numberFrame) let phoneInputFrame = countryCodeFrame.union(numberFrame)
@ -360,7 +360,6 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
self.animationNode = DefaultAnimatedStickerNodeImpl() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroPhone"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) 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 = ManagedPhoneAnimationNode()
self.managedAnimationNode.isHidden = true self.managedAnimationNode.isHidden = true
@ -479,9 +478,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
func animateIn(buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView) { func animateIn(buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView) {
self.proceedNode.animateTitle(to: self.strings.Login_Continue) 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?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
self?.animationSnapshotView?.removeFromSuperview() self?.animationSnapshotView?.removeFromSuperview()
self?.animationSnapshotView = nil self?.animationSnapshotView = nil
@ -507,7 +504,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
for node in nodes { for node in nodes {
node.alpha = 1.0 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) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
var insets = layout.insets(options: []) var insets = layout.insets(options: [])
insets.top = layout.statusBarHeight ?? 20.0 insets.top = layout.statusBarHeight ?? 20.0
if let inputHeight = layout.inputHeight, !inputHeight.isZero { if let inputHeight = layout.inputHeight, !inputHeight.isZero {
insets.bottom = max(inputHeight, insets.bottom) 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) self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
let inset: CGFloat = 24.0 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) let proceedSize = CGSize(width: layout.size.width - inset * 2.0, height: proceedHeight)
var items: [AuthorizationLayoutItem] = [ 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: titleInset, maxValue: titleInset), 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.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), 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)), 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) let contactSyncSize = self.contactSyncNode.updateLayout(width: layout.size.width)
if self.hasOtherAccounts { if self.hasOtherAccounts {
self.contactSyncNode.isHidden = false self.contactSyncNode.isHidden = false
@ -558,7 +569,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
self.animationNode.updateLayout(size: animationSize) 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) transition.updateFrame(node: self.managedAnimationNode, frame: self.animationNode.frame)
} }
@ -696,7 +707,12 @@ final class PhoneConfirmationController: ViewController {
var proceed: () -> Void = {} 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 dimNode: ASDisplayNode
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
@ -714,7 +730,14 @@ final class PhoneConfirmationController: ViewController {
var proceed: () -> Void = {} var proceed: () -> Void = {}
var cancel: () -> Void = {} var cancel: () -> Void = {}
private var validLayout: ContainerViewLayout?
init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String) { init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String) {
self.theme = theme
self.code = code
self.number = number
self.dimNode = ASDisplayNode() self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4) self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
@ -753,7 +776,6 @@ final class PhoneConfirmationController: ViewController {
self.codeTargetNode.displaysAsynchronously = false self.codeTargetNode.displaysAsynchronously = false
self.codeTargetNode.attributedText = NSAttributedString(string: code, font: largeFont, textColor: theme.list.itemPrimaryTextColor) self.codeTargetNode.attributedText = NSAttributedString(string: code, font: largeFont, textColor: theme.list.itemPrimaryTextColor)
self.phoneTargetNode = ImmediateTextNode() self.phoneTargetNode = ImmediateTextNode()
self.phoneTargetNode.displaysAsynchronously = false self.phoneTargetNode.displaysAsynchronously = false
@ -801,6 +823,9 @@ final class PhoneConfirmationController: ViewController {
} }
func animateIn(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode) { func animateIn(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode) {
guard let layout = self.validLayout else {
return
}
let codeFrame = codeNode.convert(codeNode.bounds, to: nil) let codeFrame = codeNode.convert(codeNode.bounds, to: nil)
let numberFrame = numberNode.convert(numberNode.bounds, to: nil) let numberFrame = numberNode.convert(numberNode.bounds, to: nil)
let buttonFrame = buttonNode.convert(buttonNode.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) 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) 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) 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 targetScale = codeSize.height / self.codeTargetNode.frame.height
let sourceScale = self.codeTargetNode.frame.height / codeSize.height let sourceScale = self.codeTargetNode.frame.height / codeSize.height
self.codeSourceNode.layer.animateScale(from: 1.0, to: sourceScale, 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: 0.3) 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: 0.3) 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.animateScale(from: 1.0, to: sourceScale, duration: duration)
self.phoneSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) 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: 0.3) 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.animateScale(from: targetScale, to: 1.0, duration: duration)
self.codeTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) 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: 0.3) 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.animateScale(from: targetScale, to: 1.0, duration: duration)
self.phoneTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) 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: 0.3) 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.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.animateAlpha(from: 0.0, to: 1.0, duration: duration)
self.textNode.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3) 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: 0.3, additive: true) 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.animateAlpha(from: 0.0, to: 1.0, duration: duration)
self.cancelButton.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3) 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: 0.3, additive: true) 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) { 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) 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) 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) 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 targetScale = codeSize.height / self.codeTargetNode.frame.height
let sourceScale = self.codeTargetNode.frame.height / codeSize.height let sourceScale = self.codeTargetNode.frame.height / codeSize.height
self.codeSourceNode.layer.animateScale(from: sourceScale, to: 1.0, 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: 0.3) 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: 0.3) 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.animateScale(from: sourceScale, to: 1.0, duration: duration)
self.phoneSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) 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: 0.3) 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.animateScale(from: 1.0, to: targetScale, duration: duration)
self.codeTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) 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: 0.3) 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 codeNode.isHidden = false
numberNode.isHidden = false numberNode.isHidden = false
buttonNode.isHidden = false buttonNode.isHidden = false
} }
self.phoneTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3) self.phoneTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: duration)
self.phoneTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in self.phoneTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { _ in
completion() 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.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.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.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: 0.3, removeOnCompletion: false, additive: true) 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.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.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: 0.3, removeOnCompletion: false, additive: true) 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) { func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let hadLayout = self.validLayout != nil
self.validLayout = layout
let sideInset: CGFloat = 8.0 let sideInset: CGFloat = 8.0
let innerInset: CGFloat = 18.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 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) 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) 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 spacing: CGFloat = 10.0
let numberSize = self.phoneTargetNode.updateLayout(backgroundSize)
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) 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)) 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)) transition.updateFrame(node: self.phoneTargetNode, frame: numberFrame.offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
let textSize = self.textNode.updateLayout(backgroundSize) let textSize = self.textNode.updateLayout(backgroundSize)
@ -972,6 +1021,10 @@ final class PhoneConfirmationController: ViewController {
} }
} }
func dismissAnimated() {
self.controllerNode.cancel()
}
func transitionOut() { func transitionOut() {
self.controllerNode.cancel() self.controllerNode.cancel()

View File

@ -2136,21 +2136,21 @@ open class TextView: UIView {
} }
} }
if !line.strikethroughs.isEmpty { // if !line.strikethroughs.isEmpty {
for strikethrough in line.strikethroughs { // for strikethrough in line.strikethroughs {
var textColor: UIColor? // var textColor: UIColor?
layout.attributedString?.enumerateAttributes(in: NSMakeRange(line.range.location, line.range.length), options: []) { attributes, range, _ in // 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 { // if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor {
textColor = color // textColor = color
} // }
} // }
if let textColor = textColor { // if let textColor = textColor {
context.setFillColor(textColor.cgColor) // context.setFillColor(textColor.cgColor)
} // }
let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) // 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)) // context.fill(CGRect(x: frame.minX, y: frame.minY - 5.0, width: frame.width, height: 1.0))
} // }
} // }
if !line.spoilers.isEmpty { if !line.spoilers.isEmpty {
if layout.displaySpoilers { if layout.displaySpoilers {

View File

@ -10,6 +10,7 @@ import PersistentStringHash
private let productIdentifiers = [ private let productIdentifiers = [
"org.telegram.telegramPremium.annual", "org.telegram.telegramPremium.annual",
"org.telegram.telegramPremium.semiannual",
"org.telegram.telegramPremium.monthly", "org.telegram.telegramPremium.monthly",
"org.telegram.telegramPremium.twelveMonths", "org.telegram.telegramPremium.twelveMonths",
"org.telegram.telegramPremium.sixMonths", "org.telegram.telegramPremium.sixMonths",
@ -17,7 +18,7 @@ private let productIdentifiers = [
] ]
private func isSubscriptionProductId(_ id: String) -> Bool { 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 { private extension NSDecimalNumber {
@ -30,6 +31,22 @@ private extension NSDecimalNumber {
raiseOnUnderflow: false, raiseOnUnderflow: false,
raiseOnDivideByZero: 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 { public final class InAppPurchaseManager: NSObject {
@ -57,7 +74,7 @@ public final class InAppPurchaseManager: NSObject {
} else if #available(iOS 11.2, *) { } else if #available(iOS 11.2, *) {
return self.skProduct.subscriptionPeriod != nil return self.skProduct.subscriptionPeriod != nil
} else { } 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 { 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) ?? "" return self.numberFormatter.string(from: price) ?? ""
} }
public func defaultPrice(_ value: NSDecimalNumber, monthsCount: Int) -> String { public func defaultPrice(_ value: NSDecimalNumber, monthsCount: Int) -> String {
let price = value.multiplying(by: NSDecimalNumber(value: monthsCount)).round(2) 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 { public var priceValue: NSDecimalNumber {

View File

@ -548,7 +548,6 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
).start(next: { [weak self] products, peer in ).start(next: { [weak self] products, peer in
if let strongSelf = self { if let strongSelf = self {
var gifts: [PremiumGiftProduct] = [] var gifts: [PremiumGiftProduct] = []
for option in strongSelf.options { for option in strongSelf.options {
if let product = products.first(where: { $0.id == option.storeProductId }), !product.isSubscription { if let product = products.first(where: { $0.id == option.storeProductId }), !product.isSubscription {

View File

@ -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 { final class PremiumOptionComponent: CombinedComponent {
let title: String let title: String
let subtitle: String let subtitle: String
@ -419,7 +440,7 @@ final class PremiumOptionComponent: CombinedComponent {
) )
var spacing: CGFloat = 0.0 var spacing: CGFloat = 0.0
var subtitleHeight: CGFloat = 0.0 var subtitleSize = CGSize()
if !component.subtitle.isEmpty { if !component.subtitle.isEmpty {
spacing = 2.0 spacing = 2.0
@ -447,7 +468,7 @@ final class PremiumOptionComponent: CombinedComponent {
context.add(subtitle 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)) .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.top -= 2.0
insets.bottom -= 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)) .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 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 context.add(check
@ -970,20 +1000,22 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
let source: PremiumSource let source: PremiumSource
let isPremium: Bool? let isPremium: Bool?
let otherPeerName: String? let otherPeerName: String?
let products: [InAppPurchaseManager.Product]? let products: [PremiumProduct]?
let selectedProductId: String? let selectedProductId: String?
let promoConfiguration: PremiumPromoConfiguration?
let present: (ViewController) -> Void let present: (ViewController) -> Void
let selectProduct: (String) -> Void let selectProduct: (String) -> Void
let buy: () -> Void let buy: () -> Void
let updateIsFocused: (Bool) -> 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.context = context
self.source = source self.source = source
self.isPremium = isPremium self.isPremium = isPremium
self.otherPeerName = otherPeerName self.otherPeerName = otherPeerName
self.products = products self.products = products
self.selectedProductId = selectedProductId self.selectedProductId = selectedProductId
self.promoConfiguration = promoConfiguration
self.present = present self.present = present
self.selectProduct = selectProduct self.selectProduct = selectProduct
self.buy = buy self.buy = buy
@ -1009,6 +1041,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
if lhs.selectedProductId != rhs.selectedProductId { if lhs.selectedProductId != rhs.selectedProductId {
return false return false
} }
if lhs.promoConfiguration != rhs.promoConfiguration {
return false
}
return true return true
} }
@ -1016,15 +1051,14 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
final class State: ComponentState { final class State: ComponentState {
private let context: AccountContext private let context: AccountContext
var products: [InAppPurchaseManager.Product]? var products: [PremiumProduct]?
var selectedProductId: String? var selectedProductId: String?
var isPremium: Bool? var isPremium: Bool?
private var disposable: Disposable? private var disposable: Disposable?
private(set) var configuration = PremiumIntroConfiguration.defaultValue private(set) var configuration = PremiumIntroConfiguration.defaultValue
private(set) var promoConfiguration: PremiumPromoConfiguration?
private var stickersDisposable: Disposable? private var stickersDisposable: Disposable?
private var preloadDisposableSet = DisposableSet() private var preloadDisposableSet = DisposableSet()
@ -1042,13 +1076,11 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
super.init() super.init()
self.disposable = (context.engine.data.subscribe( self.disposable = (context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Configuration.App(), TelegramEngine.EngineData.Item.Configuration.App()
TelegramEngine.EngineData.Item.Configuration.PremiumPromo()
) )
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration, promoConfiguration in |> deliverOnMainQueue).start(next: { [weak self] appConfiguration in
if let strongSelf = self { if let strongSelf = self {
strongSelf.configuration = PremiumIntroConfiguration.with(appConfiguration: appConfiguration) strongSelf.configuration = PremiumIntroConfiguration.with(appConfiguration: appConfiguration)
strongSelf.promoConfiguration = promoConfiguration
strongSelf.updated(transition: .immediate) strongSelf.updated(transition: .immediate)
if let identifier = source.identifier { 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) 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) let shortestOptionPrice: (Int64, NSDecimalNumber)
if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) { 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 { } else {
shortestOptionPrice = (1, NSDecimalNumber(decimal: 1)) shortestOptionPrice = (1, NSDecimalNumber(decimal: 1))
} }
@ -1261,16 +1289,17 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
var i = 0 var i = 0
for product in products { for product in products {
let giftTitle: String let giftTitle: String
let months: Float let months = product.months
if product.id.hasSuffix(".monthly") { if product.id.hasSuffix(".monthly") {
giftTitle = strings.Premium_Monthly giftTitle = strings.Premium_Monthly
months = 1 } else if product.id.hasSuffix(".semiannual") {
giftTitle = strings.Premium_Semiannual
} else { } else {
giftTitle = strings.Premium_Annual 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 let discount: String
if discountValue > 0 { if discountValue > 0 {
discount = "-\(discountValue)%" discount = "-\(discountValue)%"
@ -1278,12 +1307,12 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
discount = "" discount = ""
} }
let defaultPrice = product.defaultPrice(shortestOptionPrice.1, monthsCount: Int(months)) let defaultPrice = product.storeProduct.defaultPrice(shortestOptionPrice.1, monthsCount: Int(months))
var subtitle = "" var subtitle = ""
var pricePerMonth = product.price var pricePerMonth = product.price
if months > 1 { if months > 1 {
pricePerMonth = product.pricePerMonth(Int(months)) pricePerMonth = product.storeProduct.pricePerMonth(Int(months))
if discountValue > 0 { if discountValue > 0 {
subtitle = "**\(defaultPrice)** \(product.price)" subtitle = "**\(defaultPrice)** \(product.price)"
@ -1552,7 +1581,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
let termsString: MultilineTextComponent.TextContent let termsString: MultilineTextComponent.TextContent
if isGiftView { if isGiftView {
termsString = .plain(NSAttributedString()) 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) 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) termsString = .plain(attributedString)
} else { } else {
@ -1723,8 +1752,10 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
var inProgress = false var inProgress = false
var products: [InAppPurchaseManager.Product]? private(set) var promoConfiguration: PremiumPromoConfiguration?
var selectedProductId: String?
private(set) var products: [PremiumProduct]?
private(set) var selectedProductId: String?
var isPremium: Bool? var isPremium: Bool?
var otherPeerName: String? var otherPeerName: String?
@ -1740,6 +1771,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
private var disposable: Disposable? private var disposable: Disposable?
private var paymentDisposable = MetaDisposable() private var paymentDisposable = MetaDisposable()
private var activationDisposable = MetaDisposable() private var activationDisposable = MetaDisposable()
private var preloadDisposableSet = DisposableSet()
var price: String? { var price: String? {
return self.products?.first(where: { $0.id == self.selectedProductId })?.price return self.products?.first(where: { $0.id == self.selectedProductId })?.price
@ -1792,20 +1824,37 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
self.disposable = combineLatest( self.disposable = combineLatest(
queue: Queue.mainQueue(), queue: Queue.mainQueue(),
availableProducts, availableProducts,
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.PremiumPromo()),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|> map { peer -> Bool in |> map { peer -> Bool in
return peer?.isPremium ?? false return peer?.isPremium ?? false
}, },
otherPeerName otherPeerName
).start(next: { [weak self] products, isPremium, otherPeerName in ).start(next: { [weak self] availableProducts, promoConfiguration, isPremium, otherPeerName in
if let strongSelf = self { if let strongSelf = self {
strongSelf.promoConfiguration = promoConfiguration
let hadProducts = strongSelf.products != nil let hadProducts = strongSelf.products != nil
strongSelf.products = products.filter { $0.isSubscription }
if !hadProducts { var products: [PremiumProduct] = []
strongSelf.selectedProductId = strongSelf.products?.last?.id 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.isPremium = isPremium
strongSelf.otherPeerName = otherPeerName 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) strongSelf.updated(transition: .immediate)
} }
}) })
@ -1833,6 +1882,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
self.paymentDisposable.dispose() self.paymentDisposable.dispose()
self.activationDisposable.dispose() self.activationDisposable.dispose()
self.emojiFileDisposable?.dispose() self.emojiFileDisposable?.dispose()
self.preloadDisposableSet.dispose()
} }
func buy() { func buy() {
@ -1851,7 +1901,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|> deliverOnMainQueue).start(next: { [weak self] available in |> deliverOnMainQueue).start(next: { [weak self] available in
if let strongSelf = self { if let strongSelf = self {
if available { if available {
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct) strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct.storeProduct)
|> deliverOnMainQueue).start(next: { [weak self] status in |> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self, case .purchased = status { if let strongSelf = self, case .purchased = status {
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId) strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
@ -2153,6 +2203,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
otherPeerName: state.otherPeerName, otherPeerName: state.otherPeerName,
products: state.products, products: state.products,
selectedProductId: state.selectedProductId, selectedProductId: state.selectedProductId,
promoConfiguration: state.promoConfiguration,
present: context.component.present, present: context.component.present,
selectProduct: { [weak state] productId in selectProduct: { [weak state] productId in
state?.selectProduct(productId) state?.selectProduct(productId)

View File

@ -539,8 +539,18 @@ private func privacyAndSecurityControllerEntries(
return entries return entries
} }
class PrivacyAndSecurityControllerImpl: ItemListController { class PrivacyAndSecurityControllerImpl: ItemListController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
var authorizationCompletion: (ASAuthorizationCredential) -> Void = { _ in }
@available(iOS 13.0, *)
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
self.authorizationCompletion(authorization)
}
@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 { 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 {
@ -1083,6 +1093,27 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, openUrl: { _ in }, back: { let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, openUrl: { _ in }, back: {
dismissCodeControllerImpl?() dismissCodeControllerImpl?()
}) })
let emailChangeCompletion = { [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
}))
}
}
}
codeController.loginWithCode = { [weak codeController] code in codeController.loginWithCode = { [weak codeController] code in
actionsDisposable.add((verifyLoginEmailChange(account: context.account, code: .emailCode(code)) actionsDisposable.add((verifyLoginEmailChange(account: context.account, code: .emailCode(code))
|> deliverOnMainQueue).start(error: { error in |> deliverOnMainQueue).start(error: { error in
@ -1120,39 +1151,57 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
} }
} }
}, completed: { [weak codeController] in }, completed: {
codeController?.animateSuccess() emailChangeCompletion()
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
}))
}
}
})) }))
} }
codeController.signInWithApple = { codeController.signInWithApple = { [weak controller, weak codeController] in
// if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
// let appleIdProvider = ASAuthorizationAppleIDProvider() let appleIdProvider = ASAuthorizationAppleIDProvider()
// let passwordProvider = ASAuthorizationPasswordProvider() let passwordProvider = ASAuthorizationPasswordProvider()
// let request = appleIdProvider.createRequest() let request = appleIdProvider.createRequest()
//
// let passwordRequest = passwordProvider.createRequest() let passwordRequest = passwordProvider.createRequest()
//
// let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest]) let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest])
// authorizationController.delegate = strongSelf authorizationController.delegate = controller
// authorizationController.presentationContextProvider = strongSelf authorizationController.presentationContextProvider = controller
// authorizationController.performRequests() authorizationController.performRequests()
// }
controller.authorizationCompletion = { [weak controller, weak codeController] credentials in
switch authorization.credential {
case let appleIdCredential as ASAuthorizationAppleIDCredential:
guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else {
codeController?.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
}
codeController?.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()
}))
default:
break
}
}
}
} }
codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, nextPhoneLoginDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil) 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) pushControllerImpl?(codeController, true)

View File

@ -432,7 +432,7 @@ public func verifyLoginEmailChange(account: Account, code: AuthorizationCode.Ema
} }
} }
|> mapToSignal { _ -> Signal<Never, AuthorizationEmailVerificationError> in |> mapToSignal { _ -> Signal<Never, AuthorizationEmailVerificationError> in
return .never() return .complete()
} }
} }