Merge commit '319c164369f57b4996942de6c3f950998a543e2f'

This commit is contained in:
Ali
2022-08-30 18:38:51 +04:00
19 changed files with 560 additions and 301 deletions
@@ -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)
}
@@ -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)
@@ -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()
@@ -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)
}
@@ -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() {
+15 -15
View File
@@ -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
@@ -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)