diff --git a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift index bc1e10b488..66c79dbfa7 100644 --- a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift +++ b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift @@ -152,6 +152,18 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode: } } +private func removePlus(_ text: String?) -> String { + var result = "" + if let text = text { + for c in text { + if c != "+" { + result += String(c) + } + } + } + return result +} + public final class AuthorizationSequenceCountrySelectionController: ViewController { static func countries() -> [Country] { return countryCodes @@ -176,6 +188,7 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll } public static func lookupCountryIdByNumber(_ number: String, preferredCountries: [String: String]) -> (Country, Country.CountryCode)? { + let number = removePlus(number) var results: [(Country, Country.CountryCode)]? = nil if number.count == 1, let preferredCountryId = preferredCountries[number], let country = lookupCountryById(preferredCountryId), let code = country.countryCodes.first { return (country, code) diff --git a/submodules/CountrySelectionUI/Sources/CountryList.swift b/submodules/CountrySelectionUI/Sources/CountryList.swift index d695900dd4..b38d6d8a64 100644 --- a/submodules/CountrySelectionUI/Sources/CountryList.swift +++ b/submodules/CountrySelectionUI/Sources/CountryList.swift @@ -1,6 +1,29 @@ import Foundation import AppBundle +public func emojiFlagForISOCountryCode(_ countryCode: String) -> String { + if countryCode.count != 2 { + return "" + } + + if countryCode == "XG" { + return "🛰️" + } else if countryCode == "XV" { + return "🌍" + } + + if ["YL"].contains(countryCode) { + return "" + } + + let base : UInt32 = 127397 + var s = "" + for v in countryCode.unicodeScalars { + s.unicodeScalars.append(UnicodeScalar(base + v.value)!) + } + return String(s) +} + private func loadCountriesInfo() -> [(Int, String, String)] { guard let filePath = getAppBundle().path(forResource: "PhoneCountries", ofType: "txt") else { return [] diff --git a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift index 56c8d7fa58..68202bbe0a 100644 --- a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift +++ b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift @@ -166,6 +166,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { public var returnAction: (() -> Void)? private let phoneFormatter = InteractivePhoneFormatter() + public var customFormatter: ((String) -> String?)? public var mask: NSAttributedString? { didSet { @@ -180,7 +181,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { if let text = self.numberField.textField.text { mutableMask.replaceCharacters(in: NSRange(location: 0, length: min(text.count, mask.string.count)), with: text) } - mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mask.string.count))) + mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mutableMask.string.count))) mutableMask.addAttribute(.kern, value: 1.6, range: NSRange(location: 0, length: mask.string.count)) self.placeholderNode.attributedText = mutableMask } else { @@ -265,7 +266,12 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { } private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) { - let (regionPrefix, text) = self.phoneFormatter.updateText(inputText) + var (regionPrefix, text) = self.phoneFormatter.updateText(inputText) + + if let customFormatter = self.customFormatter, let customRegionPrefix = customFormatter(inputText) { + regionPrefix = "+\(customRegionPrefix)" + text = inputText + } var realRegionPrefix: String var numberText: String diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index bfaa279cff..f7b4f026b9 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -87,6 +87,12 @@ final class ChangePhoneNumberController: ViewController { strongSelf.push(controller) } } + + loadServerCountryCodes(accountManager: self.context.sharedContext.accountManager, network: self.context.account.network, completion: { [weak self] in + if let strongSelf = self { + strongSelf.controllerNode.updateCountryCode() + } + }) } override func viewWillAppear(_ animated: Bool) { diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift index 05fdaaa7e7..5cabbb7e0f 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift @@ -91,6 +91,8 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { } } + var preferredCountryIdForCode: [String: String] = [:] + var selectCountryCode: (() -> Void)? var inProgress: Bool = false { @@ -155,9 +157,43 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) + let processNumberChange: (String) -> Bool = { [weak self] number in + guard let strongSelf = self else { + return false + } + if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) { + let flagString = emojiFlagForISOCountryCode(country.id) + let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.presentationData.strings) ?? country.name + strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) + + let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers]) + if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: strongSelf.presentationData.theme.list.itemPlaceholderTextColor) }) { + strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = nil + strongSelf.phoneInputNode.mask = mask + } else { + strongSelf.phoneInputNode.mask = nil + strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strongSelf.presentationData.strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: strongSelf.presentationData.theme.list.itemPlaceholderTextColor) + } + return true + } else { + return false + } + } + + self.phoneInputNode.numberTextUpdated = { [weak self] number in + if let strongSelf = self { + let _ = processNumberChange(strongSelf.phoneInputNode.number) + } + } + self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in if let strongSelf = self { - if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { + if let name = name { + strongSelf.preferredCountryIdForCode[code] = name + } + + if processNumberChange(strongSelf.phoneInputNode.number) { + } else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.presentationData.strings) ?? countryName strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) } else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] { @@ -168,6 +204,14 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { } } + self.phoneInputNode.customFormatter = { number in + if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) { + return code.code + } else { + return nil + } + } + var countryId: String? = nil let networkInfo = CTTelephonyNetworkInfo() if let carrier = networkInfo.subscriberCellularProvider { @@ -193,6 +237,10 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { self.phoneInputNode.number = "+\(countryCodeAndId.0)" } + func updateCountryCode() { + self.phoneInputNode.codeAndNumber = self.codeAndNumber + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { var insets = layout.insets(options: [.statusBar, .input]) insets.left = layout.safeInsets.left diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index 0c261fbc8e..a6dda75351 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -13,24 +13,6 @@ import SwiftSignalKit import Postbox import AccountContext -private func emojiFlagForISOCountryCode(_ countryCode: NSString) -> String { - if countryCode.length != 2 { - return "" - } - - let base: UInt32 = 127462 - 65 - let first: UInt32 = base + UInt32(countryCode.character(at: 0)) - let second: UInt32 = base + UInt32(countryCode.character(at: 1)) - - var data = Data() - data.count = 8 - data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - bytes[0] = first - bytes[1] = second - } - return String(data: data, encoding: String.Encoding.utf32LittleEndian) ?? "" -} - private final class PhoneAndCountryNode: ASDisplayNode { let strings: PresentationStrings let countryButton: ASButtonNode @@ -128,25 +110,12 @@ private final class PhoneAndCountryNode: ASDisplayNode { self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) - func removePlus(_ text: String?) -> String { - var result = "" - if let text = text { - for c in text { - if c != "+" { - result += String(c) - } - } - } - return result - } - let processNumberChange: (String) -> Bool = { [weak self] number in guard let strongSelf = self else { return false } - let number = removePlus(number) if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) { - let flagString = emojiFlagForISOCountryCode(country.id as NSString) + let flagString = emojiFlagForISOCountryCode(country.id) let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.strings) ?? country.name strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: []) @@ -178,12 +147,12 @@ private final class PhoneAndCountryNode: ASDisplayNode { if processNumberChange(strongSelf.phoneInputNode.number) { } else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { - let flagString = emojiFlagForISOCountryCode(name as NSString) + let flagString = emojiFlagForISOCountryCode(name) let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: []) strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor) } else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] { - let flagString = emojiFlagForISOCountryCode(countryId as NSString) + let flagString = emojiFlagForISOCountryCode(countryId) let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: []) strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor) @@ -195,6 +164,14 @@ private final class PhoneAndCountryNode: ASDisplayNode { } } + self.phoneInputNode.customFormatter = { number in + if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) { + return code.code + } else { + return nil + } + } + self.phoneInputNode.number = "+1" self.phoneInputNode.returnAction = { [weak self] in self?.checkPhone?() @@ -366,10 +343,6 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.tokenEventsDisposable.dispose() } - func updateCountryCode() { - self.phoneAndCountryNode.phoneInputNode.codeAndNumber = self.codeAndNumber - } - override func didLoad() { super.didLoad() @@ -379,6 +352,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { #endif } + func updateCountryCode() { + self.phoneAndCountryNode.phoneInputNode.codeAndNumber = self.codeAndNumber + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { var insets = layout.insets(options: []) insets.top = navigationBarHeight